1 /* Copyright (C) 1997, 1998 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. */
31 #include <sys/param.h>
32 #include <sys/select.h>
33 #include <sys/socket.h>
35 #include <sys/types.h>
41 #include "utmpd-private.h"
44 #define DEFAULT_USER "daemon"
47 /* Get libc version number. */
50 #define PACKAGE _libc_intl_domainname
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' },
61 /* The UTMP database. */
62 utmp_database *utmp_db;
64 /* The socket for read only requests. */
67 /* The socket for read/write requests. */
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);
82 main (int argc, char *argv[])
90 /* Set locale via LC_ALL. */
91 setlocale (LC_ALL, "");
93 /* Set the text message domain. */
96 /* Initialize local variables. */
101 while ((opt = getopt_long (argc, argv, "dhV", long_options, NULL)) != -1)
104 case '\0': /* Long option. */
116 usage (EXIT_FAILURE);
119 /* Version information is reequested. */
122 printf ("utmpd (GNU %s) %s\n", PACKAGE, VERSION);
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\
128 printf (_("Written by %s.\n"), "Mark Kettenis");
133 /* Help is requested. */
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);
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);
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. */
170 openlog ("utmpd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
172 if (daemon (0, 0) < 0)
173 error (EXIT_FAILURE, errno, _("cannot auto-background"));
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. */
188 /* Handle incoming requests. */
193 /* Display usage information and exit. */
197 if (status != EXIT_SUCCESS)
198 fprintf (stderr, _("Try `%s --help' for more information.\n"),
199 program_invocation_name);
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);
209 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"),
217 /* Drop priviliges. */
219 drop_priviliges (void)
223 pw = getpwnam (DEFAULT_USER);
226 seteuid (pw->pw_uid);
227 setegid (pw->pw_gid);
232 /* Make a socket in the file namespace using the filename NAME as the
235 make_socket (const char *name)
237 struct sockaddr_un addr;
241 /* Create the socket. */
242 sock = socket (PF_UNIX, SOCK_STREAM, 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);
264 /* Hanlde incoming requests. */
266 void handle_requests (void)
268 client_connection *connection;
269 fd_set active_read_fd_set;
270 fd_set active_write_fd_set;
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);
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);
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))
357 termination_handler (int signum)
363 /* Restore user id. */
366 /* Clean up the files created by `bind'. */
367 unlink (_PATH_UTMPD_RO);
368 unlink (_PATH_UTMPD_RW);
371 close_database (utmp_db);
373 /* Clean up pid file. */
374 unlink (_PATH_UTMPDPID);
380 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
382 check_pid (const char *file)
386 fp = fopen (_PATH_UTMPDPID, "r");
391 fscanf (fp, "%d", &pid);
394 if (kill (pid, 0) == 0)
401 /* Write the current process id to the file FILE. Returns 0 if
402 successful, -1 if not. */
404 write_pid (const char *file)
408 fp = fopen (_PATH_UTMPDPID, "w");
412 fprintf (fp, "%d\n", getpid ());