Update.
[glibc.git] / login / programs / utmpd.c
blobca310a21de29221874231c19d267fc6023ec2f91
1 /* Copyright (C) 1997 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/select.h>
32 #include <sys/socket.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/un.h>
36 #include <syslog.h>
37 #include <unistd.h>
39 #include "utmpd.h"
40 #include "utmpd-private.h"
42 #ifndef DEFAULT_USER
43 #define DEFAULT_USER "daemon"
44 #endif
46 /* Get libc version number. */
47 #include "../../version.h"
49 #define PACKAGE _libc_intl_domainname
51 /* Long options. */
52 static const struct option long_options[] =
54 { "debug", no_argument, NULL, 'd' },
55 { "help", no_argument, NULL, 'h' },
56 { "version", no_argument, NULL, 'V' },
57 { NULL, 0, NULL, 0}
60 /* The UTMP database. */
61 utmp_database *utmp_db;
63 /* The socket for read only requests. */
64 int ro_sock = -1;
66 /* The socket for read/write requests. */
67 int rw_sock = -1;
70 /* Prototypes for the local functions. */
71 static void usage (int status) __attribute__ ((noreturn));
72 static void drop_priviliges (void);
73 static int make_socket (const char *name);
74 static void handle_requests (void) __attribute__ ((noreturn));
75 static void termination_handler (int signum);
76 static int check_pid (const char *file);
77 static int write_pid (const char *file);
80 int
81 main (int argc, char *argv[])
83 mode_t mask;
84 int debug;
85 int do_help;
86 int do_version;
87 int opt;
89 /* Set locale via LC_ALL. */
90 setlocale (LC_ALL, "");
92 /* Set the text message domain. */
93 textdomain (PACKAGE);
95 /* Initialize local variables. */
96 debug = 0;
97 do_help = 0;
98 do_version = 0;
100 while ((opt = getopt_long (argc, argv, "dhV", long_options, NULL)) != -1)
101 switch (opt)
103 case '\0': /* Long option. */
104 break;
105 case 'h':
106 do_help = 1;
107 break;
108 case 'd':
109 debug = 1;
110 break;
111 case 'V':
112 do_version = 1;
113 break;
114 default:
115 usage (EXIT_FAILURE);
118 /* Version information is reequested. */
119 if (do_version)
121 printf ("utmpd (GNU %s) %s\n", PACKAGE, VERSION);
122 printf (gettext ("\
123 Copyright (C) %s Free Software Foundation, Inc.\n\
124 This is free software; see the source for copying conditions. There is NO\n\
125 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
126 "), "1997");
127 printf (gettext ("Written by %s.\n"), "Mark Kettenis");
129 exit (EXIT_SUCCESS);
132 /* Help is requested. */
133 if (do_help)
134 usage (EXIT_SUCCESS);
136 signal (SIGINT, termination_handler);
137 signal (SIGTERM, termination_handler);
139 /* Check if we are already running. */
140 if (check_pid (_PATH_UTMPDPID))
141 error (EXIT_FAILURE, 0, "already running");
143 /* Open UTMP database. */
144 utmp_db = open_database (_PATH_UTMP "x", _PATH_UTMP);
145 if (utmp_db == NULL)
146 error (EXIT_FAILURE, errno, "%s", _PATH_UTMP);
148 /* Create sockets, with the right permissions. */
149 mask = umask (S_IXUSR | S_IXGRP | S_IXOTH);
150 ro_sock = make_socket (_PATH_UTMPD_RO);
151 umask (S_IXUSR | S_IRWXG | S_IRWXO);
152 rw_sock = make_socket (_PATH_UTMPD_RW);
153 umask (mask);
155 /* Set the sockets up to accept connections. */
156 if (listen (ro_sock, MAX_CONNECTIONS) < 0
157 || listen (rw_sock, MAX_CONNECTIONS) < 0)
158 error (EXIT_FAILURE, errno, "cannot enable socket to accept connections");
160 /* Behave like a daemon. */
161 if (!debug)
163 openlog ("utmpd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
165 if (daemon (0, 0) < 0)
166 error (EXIT_FAILURE, errno, "cannot auto-background");
167 forked = 1;
169 if (write_pid (_PATH_UTMPDPID) < 0)
170 warning (errno, "%s", _PATH_UTMPDPID);
173 /* Drop priviliges. */
174 drop_priviliges ();
176 /* Handle incoming requests. */
177 handle_requests ();
181 /* Display usage information and exit. */
182 static void
183 usage (int status)
185 if (status != EXIT_SUCCESS)
186 fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
187 program_invocation_name);
188 else
190 printf (gettext ("\
191 Usage: %s [OPTION]...\n\
192 -d, --debug do not fork and display messages on the current tty\n\
193 -h, --help display this help and exit\n\
194 -V, --version output version information and exit\n"),
195 program_invocation_name);
196 fputs (gettext ("\
197 Report bugs to <kettenis@phys.uva.nl>.\n"),
198 stdout);
201 exit (status);
205 /* Drop priviliges. */
206 static void
207 drop_priviliges (void)
209 struct passwd *pw;
211 pw = getpwnam (DEFAULT_USER);
212 if (pw)
214 seteuid (pw->pw_uid);
215 setegid (pw->pw_gid);
220 /* Make a socket in the file namespace using the filename NAME as the
221 socket's address. */
222 static int
223 make_socket (const char *name)
225 struct sockaddr_un addr;
226 size_t size;
227 int sock;
229 /* Create the socket. */
230 sock = socket (PF_UNIX, SOCK_STREAM, 0);
231 if (sock < 0)
232 error (EXIT_FAILURE, errno, "cannot create socket");
234 /* Bind a name to the socket. */
235 addr.sun_family = AF_UNIX;
236 strcpy (addr.sun_path, name);
238 /* The size of the address is the offset of the start
239 of the filename, plus its length, plus one for the
240 terminating null byte. */
241 size = (offsetof (struct sockaddr_un, sun_path)
242 + strlen (addr.sun_path));
244 if (bind (sock, (struct sockaddr *) &addr, size) < 0)
245 error (EXIT_FAILURE, errno, "%s", name);
247 return sock;
251 /* Hanlde incoming requests. */
252 static
253 void handle_requests (void)
255 client_connection *connection;
256 fd_set active_read_fd_set;
257 fd_set active_write_fd_set;
258 fd_set read_fd_set;
259 fd_set write_fd_set;
260 int fd;
262 /* Initialize the set of active sockets. */
263 FD_ZERO (&active_read_fd_set);
264 FD_ZERO (&active_write_fd_set);
265 FD_SET (rw_sock, &active_read_fd_set);
266 FD_SET (ro_sock, &active_read_fd_set);
268 while (1)
270 /* Block until input arrives on one or more active sockets. */
271 read_fd_set = active_read_fd_set;
272 write_fd_set = active_write_fd_set;
273 if (select (FD_SETSIZE, &read_fd_set, &write_fd_set, NULL, NULL) < 0)
274 error (EXIT_FAILURE, errno, "cannot get input on sockets");
276 /* Service all the sockets with input pending. */
277 for (fd = 0; fd < FD_SETSIZE; fd++)
279 if (FD_ISSET (fd, &read_fd_set))
281 if (fd == ro_sock || fd == rw_sock)
283 int access = ((fd == rw_sock) ? (R_OK | W_OK) : R_OK);
285 connection = accept_connection (fd, access);
286 if (connection == NULL)
287 error (0, errno, "cannot accept connection");
289 FD_SET (connection->sock, &active_read_fd_set);
291 else
293 connection = find_connection (fd);
294 if (connection == NULL)
295 error (EXIT_FAILURE, 0, "cannot find connection");
297 if (read_data (connection) < 0)
299 close_connection (connection);
300 FD_CLR (fd, &active_read_fd_set);
301 FD_CLR (fd, &active_write_fd_set);
304 if (connection->write_ptr > connection->write_base)
305 FD_SET (fd, &active_write_fd_set);
308 if (FD_ISSET (fd, &write_fd_set) &&
309 fd != rw_sock && fd != ro_sock)
311 connection = find_connection (fd);
312 if (connection == NULL)
313 error (EXIT_FAILURE, 0, "cannot find connection");
315 if (write_data (connection) < 0)
317 close_connection (connection);
318 FD_CLR (fd, &active_read_fd_set);
319 FD_CLR (fd, &active_write_fd_set);
322 if (connection->write_ptr == connection->write_base)
323 FD_CLR (fd, &active_write_fd_set);
330 /* Cleanup. */
331 static void
332 termination_handler (int signum)
334 /* Close sockets. */
335 close (ro_sock);
336 close (rw_sock);
338 /* Restore user id. */
339 seteuid (getuid ());
341 /* Clean up the files created by `bind'. */
342 unlink (_PATH_UTMPD_RO);
343 unlink (_PATH_UTMPD_RW);
345 if (utmp_db)
346 close_database (utmp_db);
348 /* Clean up pid file. */
349 unlink (_PATH_UTMPDPID);
351 exit (EXIT_SUCCESS);
355 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
356 static int
357 check_pid (const char *file)
359 FILE *fp;
361 fp = fopen (_PATH_UTMPDPID, "r");
362 if (fp)
364 pid_t pid;
366 fscanf (fp, "%d", &pid);
367 fclose (fp);
369 if (kill (pid, 0) == 0)
370 return 1;
373 return 0;
376 /* Write the current process id to the file FILE. Returns 0 if
377 successful, -1 if not. */
378 static int
379 write_pid (const char *file)
381 FILE *fp;
383 fp = fopen (_PATH_UTMPDPID, "w");
384 if (fp == NULL)
385 return -1;
387 fprintf (fp, "%d\n", getpid ());
388 if (ferror (fp))
389 return -1;
391 fclose (fp);
393 return 0;