Update.
[glibc.git] / login / programs / utmpd.c
blobec16b9e778181ddeec388a789fe5ec4c559eb8b7
1 /* Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <libintl.h>
24 #include <locale.h>
25 #include <pwd.h>
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <signal.h>
30 #include <string.h>
31 #include <sys/param.h>
32 #include <sys/select.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <sys/un.h>
37 #include <syslog.h>
38 #include <unistd.h>
40 #include "utmpd.h"
41 #include "utmpd-private.h"
43 #ifndef DEFAULT_USER
44 #define DEFAULT_USER "daemon"
45 #endif
47 /* Get libc version number. */
48 #include <version.h>
50 #define PACKAGE _libc_intl_domainname
52 /* Long options. */
53 static const struct option long_options[] =
55 { "debug", no_argument, NULL, 'd' },
56 { "help", no_argument, NULL, 'h' },
57 { "version", no_argument, NULL, 'V' },
58 { NULL, 0, NULL, 0}
61 /* The UTMP database. */
62 utmp_database *utmp_db;
64 /* The socket for read only requests. */
65 int ro_sock = -1;
67 /* The socket for read/write requests. */
68 int rw_sock = -1;
71 /* Prototypes for the local functions. */
72 static void usage (int status) __attribute__ ((noreturn));
73 static void drop_priviliges (void);
74 static int make_socket (const char *name);
75 static void handle_requests (void) __attribute__ ((noreturn));
76 static void termination_handler (int signum);
77 static int check_pid (const char *file);
78 static int write_pid (const char *file);
81 int
82 main (int argc, char *argv[])
84 mode_t mask;
85 int debug;
86 int do_help;
87 int do_version;
88 int opt;
90 /* Set locale via LC_ALL. */
91 setlocale (LC_ALL, "");
93 /* Set the text message domain. */
94 textdomain (PACKAGE);
96 /* Initialize local variables. */
97 debug = 0;
98 do_help = 0;
99 do_version = 0;
101 while ((opt = getopt_long (argc, argv, "dhV", long_options, NULL)) != -1)
102 switch (opt)
104 case '\0': /* Long option. */
105 break;
106 case 'h':
107 do_help = 1;
108 break;
109 case 'd':
110 debug = 1;
111 break;
112 case 'V':
113 do_version = 1;
114 break;
115 default:
116 usage (EXIT_FAILURE);
119 /* Version information is reequested. */
120 if (do_version)
122 printf ("utmpd (GNU %s) %s\n", PACKAGE, VERSION);
123 printf (_("\
124 Copyright (C) %s Free Software Foundation, Inc.\n\
125 This is free software; see the source for copying conditions. There is NO\n\
126 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
127 "), "1999");
128 printf (_("Written by %s.\n"), "Mark Kettenis");
130 exit (EXIT_SUCCESS);
133 /* Help is requested. */
134 if (do_help)
135 usage (EXIT_SUCCESS);
137 signal (SIGINT, termination_handler);
138 signal (SIGQUIT, termination_handler);
139 signal (SIGTERM, termination_handler);
141 /* Check if we are already running. */
142 if (check_pid (_PATH_UTMPDPID))
143 error (EXIT_FAILURE, 0, _("already running"));
145 /* Cleanup files created by a previous `bind'. */
146 unlink (_PATH_UTMPD_RO);
147 unlink (_PATH_UTMPD_RW);
149 /* Open UTMP database. */
150 utmp_db = open_database (_PATH_UTMP "x", _PATH_UTMP);
151 if (utmp_db == NULL)
152 exit (EXIT_FAILURE);
154 /* Create sockets, with the right permissions. */
155 mask = umask (S_IXUSR | S_IXGRP | S_IXOTH);
156 ro_sock = make_socket (_PATH_UTMPD_RO);
157 umask (S_IXUSR | S_IRWXG | S_IRWXO);
158 rw_sock = make_socket (_PATH_UTMPD_RW);
159 umask (mask);
161 /* Set the sockets up to accept connections. */
162 if (listen (ro_sock, MAX_CONNECTIONS) < 0
163 || listen (rw_sock, MAX_CONNECTIONS) < 0)
164 error (EXIT_FAILURE, errno,
165 _("cannot enable socket to accept connections"));
167 /* Behave like a daemon. */
168 if (!debug)
170 openlog ("utmpd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
172 if (daemon (0, 0) < 0)
173 error (EXIT_FAILURE, errno, _("cannot auto-background"));
174 forked = 1;
176 if (write_pid (_PATH_UTMPDPID) < 0)
177 warning (errno, "%s", _PATH_UTMPDPID);
179 /* Ignore job control signals. */
180 signal (SIGTTOU, SIG_IGN);
181 signal (SIGTTIN, SIG_IGN);
182 signal (SIGTSTP, SIG_IGN);
185 /* Drop priviliges. */
186 drop_priviliges ();
188 /* Handle incoming requests. */
189 handle_requests ();
193 /* Display usage information and exit. */
194 static void
195 usage (int status)
197 if (status != EXIT_SUCCESS)
198 fprintf (stderr, _("Try `%s --help' for more information.\n"),
199 program_invocation_name);
200 else
202 printf (_("\
203 Usage: %s [OPTION]...\n\
204 -d, --debug do not fork and display messages on the current tty\n\
205 -h, --help display this help and exit\n\
206 -V, --version output version information and exit\n"),
207 program_invocation_name);
208 fputs (_("\
209 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"),
210 stdout);
213 exit (status);
217 /* Drop priviliges. */
218 static void
219 drop_priviliges (void)
221 struct passwd *pw;
223 pw = getpwnam (DEFAULT_USER);
224 if (pw)
226 seteuid (pw->pw_uid);
227 setegid (pw->pw_gid);
232 /* Make a socket in the file namespace using the filename NAME as the
233 socket's address. */
234 static int
235 make_socket (const char *name)
237 struct sockaddr_un addr;
238 size_t size;
239 int sock;
241 /* Create the socket. */
242 sock = socket (PF_UNIX, SOCK_STREAM, 0);
243 if (sock < 0)
244 error (EXIT_FAILURE, errno, _("cannot create socket"));
246 /* Bind a name to the socket. */
247 addr.sun_family = AF_UNIX;
248 strcpy (addr.sun_path, name);
250 /* The size of the address is the offset of the start
251 of the filename, plus its length, plus one for the
252 terminating null byte. */
253 size = (offsetof (struct sockaddr_un, sun_path)
254 + strlen (addr.sun_path));
257 if (bind (sock, (struct sockaddr *) &addr, size) < 0)
258 error (EXIT_FAILURE, errno, "%s", name);
260 return sock;
264 /* Hanlde incoming requests. */
265 static
266 void handle_requests (void)
268 client_connection *connection;
269 fd_set active_read_fd_set;
270 fd_set active_write_fd_set;
271 fd_set read_fd_set;
272 fd_set write_fd_set;
273 int fd;
274 int maxfd; /* Highest used fd to optimize select/loop. */
276 /* Initialize the set of active sockets. */
277 FD_ZERO (&active_read_fd_set);
278 FD_ZERO (&active_write_fd_set);
279 FD_SET (rw_sock, &active_read_fd_set);
280 FD_SET (ro_sock, &active_read_fd_set);
282 maxfd = MAX (rw_sock, ro_sock);
284 while (1)
286 /* Block until input arrives on one or more active sockets. */
287 read_fd_set = active_read_fd_set;
288 write_fd_set = active_write_fd_set;
289 if (select (maxfd + 1, &read_fd_set, &write_fd_set, NULL, NULL) < 0)
290 error (EXIT_FAILURE, errno, _("cannot get input on sockets"));
292 /* Service all the sockets with input pending. */
293 for (fd = 0; fd <= maxfd; ++fd)
295 if (FD_ISSET (fd, &read_fd_set))
297 if (fd == ro_sock || fd == rw_sock)
299 int access = ((fd == rw_sock) ? (R_OK | W_OK) : R_OK);
301 connection = accept_connection (fd, access);
302 if (connection == NULL)
303 error (0, errno, _("cannot accept connection"));
305 FD_SET (connection->sock, &active_read_fd_set);
306 maxfd = MAX (maxfd, connection->sock);
308 else
310 connection = find_connection (fd);
311 if (connection == NULL)
312 error (EXIT_FAILURE, 0, _("cannot find connection"));
314 if (read_data (connection) < 0)
316 close_connection (connection);
317 FD_CLR (fd, &active_read_fd_set);
318 FD_CLR (fd, &active_write_fd_set);
321 if (connection->write_ptr > connection->write_base)
322 FD_SET (fd, &active_write_fd_set);
325 if (FD_ISSET (fd, &write_fd_set) &&
326 fd != rw_sock && fd != ro_sock)
328 connection = find_connection (fd);
329 if (connection == NULL)
330 error (EXIT_FAILURE, 0, _("cannot find connection"));
332 if (write_data (connection) < 0)
334 close_connection (connection);
335 FD_CLR (fd, &active_read_fd_set);
336 FD_CLR (fd, &active_write_fd_set);
339 if (connection->write_ptr == connection->write_base)
340 FD_CLR (fd, &active_write_fd_set);
344 /* Check if maxfd can be lowered. */
345 for (; maxfd >= 0; --maxfd)
347 if (FD_ISSET (maxfd, &active_read_fd_set)
348 || FD_ISSET (maxfd, &active_write_fd_set))
349 break;
355 /* Cleanup. */
356 static void
357 termination_handler (int signum)
359 /* Close sockets. */
360 close (ro_sock);
361 close (rw_sock);
363 /* Restore user id. */
364 seteuid (getuid ());
366 /* Clean up the files created by `bind'. */
367 unlink (_PATH_UTMPD_RO);
368 unlink (_PATH_UTMPD_RW);
370 if (utmp_db)
371 close_database (utmp_db);
373 /* Clean up pid file. */
374 unlink (_PATH_UTMPDPID);
376 exit (EXIT_SUCCESS);
380 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
381 static int
382 check_pid (const char *file)
384 FILE *fp;
386 fp = fopen (_PATH_UTMPDPID, "r");
387 if (fp)
389 pid_t pid;
391 fscanf (fp, "%d", &pid);
392 fclose (fp);
394 if (kill (pid, 0) == 0)
395 return 1;
398 return 0;
401 /* Write the current process id to the file FILE. Returns 0 if
402 successful, -1 if not. */
403 static int
404 write_pid (const char *file)
406 FILE *fp;
408 fp = fopen (_PATH_UTMPDPID, "w");
409 if (fp == NULL)
410 return -1;
412 fprintf (fp, "%d\n", getpid ());
413 if (ferror (fp))
414 return -1;
416 fclose (fp);
418 return 0;