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. */
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 ());