Update year in copyright message text.
[kopensolaris-gnu/glibc.git] / login / programs / utmpd.c
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.
4
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.
9
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.
14
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.  */
19
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>
39
40 #include "utmpd.h"
41 #include "utmpd-private.h"
42
43 #ifndef DEFAULT_USER
44 #define DEFAULT_USER    "daemon"
45 #endif
46
47 /* Get libc version number.  */
48 #include <version.h>
49
50 #define PACKAGE _libc_intl_domainname
51
52 /* Long options.  */
53 static const struct option long_options[] =
54 {
55   { "debug", no_argument, NULL, 'd' },
56   { "help", no_argument, NULL, 'h' },
57   { "version", no_argument, NULL, 'V' },
58   { NULL, 0, NULL, 0}
59 };
60
61 /* The UTMP database.  */
62 utmp_database *utmp_db;
63
64 /* The socket for read only requests.  */
65 int ro_sock = -1;
66
67 /* The socket for read/write requests.  */
68 int rw_sock = -1;
69
70
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);
79
80
81 int
82 main (int argc, char *argv[])
83 {
84   mode_t mask;
85   int debug;
86   int do_help;
87   int do_version;
88   int opt;
89
90   /* Set locale via LC_ALL.  */
91   setlocale (LC_ALL, "");
92
93   /* Set the text message domain.  */
94   textdomain (PACKAGE);
95
96   /* Initialize local variables.  */
97   debug = 0;
98   do_help = 0;
99   do_version = 0;
100
101   while ((opt = getopt_long (argc, argv, "dhV", long_options, NULL)) != -1)
102     switch (opt)
103       {
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);
117       }
118
119   /* Version information is reequested.  */
120   if (do_version)
121     {
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");
129
130       exit (EXIT_SUCCESS);
131     }
132
133   /* Help is requested.  */
134   if (do_help)
135     usage (EXIT_SUCCESS);
136
137   signal (SIGINT, termination_handler);
138   signal (SIGQUIT, termination_handler);
139   signal (SIGTERM, termination_handler);
140
141   /* Check if we are already running.  */
142   if (check_pid (_PATH_UTMPDPID))
143     error (EXIT_FAILURE, 0, _("already running"));
144
145   /* Cleanup files created by a previous `bind'.  */
146   unlink (_PATH_UTMPD_RO);
147   unlink (_PATH_UTMPD_RW);
148
149   /* Open UTMP database.  */
150   utmp_db = open_database (_PATH_UTMP "x", _PATH_UTMP);
151   if (utmp_db == NULL)
152     exit (EXIT_FAILURE);
153
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);
160
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"));
166
167   /* Behave like a daemon.  */
168   if (!debug)
169     {
170       openlog ("utmpd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
171
172       if (daemon (0, 0) < 0)
173         error (EXIT_FAILURE, errno, _("cannot auto-background"));
174       forked = 1;
175
176       if (write_pid (_PATH_UTMPDPID) < 0)
177         warning (errno, "%s", _PATH_UTMPDPID);
178
179       /* Ignore job control signals.  */
180       signal (SIGTTOU, SIG_IGN);
181       signal (SIGTTIN, SIG_IGN);
182       signal (SIGTSTP, SIG_IGN);
183     }
184
185   /* Drop priviliges.  */
186   drop_priviliges ();
187
188   /* Handle incoming requests.  */
189   handle_requests ();
190 }
191
192
193 /* Display usage information and exit.  */
194 static void
195 usage (int status)
196 {
197   if (status != EXIT_SUCCESS)
198     fprintf (stderr, _("Try `%s --help' for more information.\n"),
199              program_invocation_name);
200   else
201     {
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);
211     }
212
213   exit (status);
214 }
215
216
217 /* Drop priviliges.  */
218 static void
219 drop_priviliges (void)
220 {
221   struct passwd *pw;
222
223   pw = getpwnam (DEFAULT_USER);
224   if (pw)
225     {
226       seteuid (pw->pw_uid);
227       setegid (pw->pw_gid);
228     }
229 }
230
231
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)
236 {
237   struct sockaddr_un addr;
238   size_t size;
239   int sock;
240
241   /* Create the socket.  */
242   sock = socket (PF_UNIX, SOCK_STREAM, 0);
243   if (sock < 0)
244     error (EXIT_FAILURE, errno, _("cannot create socket"));
245
246   /* Bind a name to the socket.  */
247   addr.sun_family = AF_UNIX;
248   strcpy (addr.sun_path, name);
249
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));
255
256
257   if (bind (sock, (struct sockaddr *) &addr, size) < 0)
258     error (EXIT_FAILURE, errno, "%s", name);
259
260   return sock;
261 }
262
263
264 /* Hanlde incoming requests.  */
265 static
266 void handle_requests (void)
267 {
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.  */
275
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);
281
282   maxfd = MAX (rw_sock, ro_sock);
283
284   while (1)
285     {
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"));
291
292       /* Service all the sockets with input pending.  */
293       for (fd = 0; fd <= maxfd; ++fd)
294         {
295           if (FD_ISSET (fd, &read_fd_set))
296             {
297               if (fd == ro_sock || fd == rw_sock)
298                 {
299                   int access = ((fd == rw_sock) ? (R_OK | W_OK) : R_OK);
300
301                   connection = accept_connection (fd, access);
302                   if (connection == NULL)
303                     error (0, errno, _("cannot accept connection"));
304
305                   FD_SET (connection->sock, &active_read_fd_set);
306                   maxfd = MAX (maxfd, connection->sock);
307                 }
308               else
309                 {
310                   connection = find_connection (fd);
311                   if (connection == NULL)
312                     error (EXIT_FAILURE, 0, _("cannot find connection"));
313
314                   if (read_data (connection) < 0)
315                     {
316                       close_connection (connection);
317                       FD_CLR (fd, &active_read_fd_set);
318                       FD_CLR (fd, &active_write_fd_set);
319                     }
320
321                   if (connection->write_ptr > connection->write_base)
322                       FD_SET (fd, &active_write_fd_set);
323                 }
324             }
325           if (FD_ISSET (fd, &write_fd_set) &&
326               fd != rw_sock && fd != ro_sock)
327             {
328               connection = find_connection (fd);
329               if (connection == NULL)
330                 error (EXIT_FAILURE, 0, _("cannot find connection"));
331
332               if (write_data (connection) < 0)
333                 {
334                   close_connection (connection);
335                   FD_CLR (fd, &active_read_fd_set);
336                   FD_CLR (fd, &active_write_fd_set);
337                 }
338
339               if (connection->write_ptr == connection->write_base)
340                 FD_CLR (fd, &active_write_fd_set);
341             }
342         }
343
344       /* Check if maxfd can be lowered.  */
345       for (; maxfd >= 0; --maxfd)
346         {
347           if (FD_ISSET (maxfd, &active_read_fd_set)
348               || FD_ISSET (maxfd, &active_write_fd_set))
349             break;
350         }
351     }
352 }
353
354
355 /* Cleanup.  */
356 static void
357 termination_handler (int signum)
358 {
359   /* Close sockets.  */
360   close (ro_sock);
361   close (rw_sock);
362
363   /* Restore user id.  */
364   seteuid (getuid ());
365
366   /* Clean up the files created by `bind'.  */
367   unlink (_PATH_UTMPD_RO);
368   unlink (_PATH_UTMPD_RW);
369
370   if (utmp_db)
371     close_database (utmp_db);
372
373   /* Clean up pid file.  */
374   unlink (_PATH_UTMPDPID);
375
376   exit (EXIT_SUCCESS);
377 }
378
379
380 /* Returns 1 if the process in pid file FILE is running, 0 if not.  */
381 static int
382 check_pid (const char *file)
383 {
384   FILE *fp;
385
386   fp = fopen (_PATH_UTMPDPID, "r");
387   if (fp)
388     {
389       pid_t pid;
390
391       fscanf (fp, "%d", &pid);
392       fclose (fp);
393
394       if (kill (pid, 0) == 0)
395         return 1;
396     }
397
398   return 0;
399 }
400
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)
405 {
406   FILE *fp;
407
408   fp = fopen (_PATH_UTMPDPID, "w");
409   if (fp == NULL)
410     return -1;
411
412   fprintf (fp, "%d\n", getpid ());
413   if (ferror (fp))
414     return -1;
415
416   fclose (fp);
417
418   return 0;
419 }