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. */
31 #include <sys/select.h>
32 #include <sys/socket.h>
34 #include <sys/types.h>
40 #include "utmpd-private.h"
43 #define DEFAULT_USER "daemon"
46 /* Get libc version number. */
47 #include "../../version.h"
49 #define PACKAGE _libc_intl_domainname
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' },
60 /* The UTMP database. */
61 utmp_database
*utmp_db
;
63 /* The socket for read only requests. */
66 /* The socket for read/write requests. */
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
);
81 main (int argc
, char *argv
[])
89 /* Set locale via LC_ALL. */
90 setlocale (LC_ALL
, "");
92 /* Set the text message domain. */
95 /* Initialize local variables. */
100 while ((opt
= getopt_long (argc
, argv
, "dhV", long_options
, NULL
)) != -1)
103 case '\0': /* Long option. */
115 usage (EXIT_FAILURE
);
118 /* Version information is reequested. */
121 printf ("utmpd (GNU %s) %s\n", PACKAGE
, VERSION
);
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\
127 printf (gettext ("Written by %s.\n"), "Mark Kettenis");
132 /* Help is requested. */
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
);
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
);
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. */
163 openlog ("utmpd", LOG_CONS
| LOG_ODELAY
, LOG_DAEMON
);
165 if (daemon (0, 0) < 0)
166 error (EXIT_FAILURE
, errno
, "cannot auto-background");
169 if (write_pid (_PATH_UTMPDPID
) < 0)
170 warning (errno
, "%s", _PATH_UTMPDPID
);
173 /* Drop priviliges. */
176 /* Handle incoming requests. */
181 /* Display usage information and exit. */
185 if (status
!= EXIT_SUCCESS
)
186 fprintf (stderr
, gettext ("Try `%s --help' for more information.\n"),
187 program_invocation_name
);
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
);
197 Report bugs to <kettenis@phys.uva.nl>.\n"),
205 /* Drop priviliges. */
207 drop_priviliges (void)
211 pw
= getpwnam (DEFAULT_USER
);
214 seteuid (pw
->pw_uid
);
215 setegid (pw
->pw_gid
);
220 /* Make a socket in the file namespace using the filename NAME as the
223 make_socket (const char *name
)
225 struct sockaddr_un addr
;
229 /* Create the socket. */
230 sock
= socket (PF_UNIX
, SOCK_STREAM
, 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
);
251 /* Hanlde incoming requests. */
253 void handle_requests (void)
255 client_connection
*connection
;
256 fd_set active_read_fd_set
;
257 fd_set active_write_fd_set
;
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
);
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
);
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
);
332 termination_handler (int signum
)
338 /* Restore user id. */
341 /* Clean up the files created by `bind'. */
342 unlink (_PATH_UTMPD_RO
);
343 unlink (_PATH_UTMPD_RW
);
346 close_database (utmp_db
);
348 /* Clean up pid file. */
349 unlink (_PATH_UTMPDPID
);
355 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
357 check_pid (const char *file
)
361 fp
= fopen (_PATH_UTMPDPID
, "r");
366 fscanf (fp
, "%d", &pid
);
369 if (kill (pid
, 0) == 0)
376 /* Write the current process id to the file FILE. Returns 0 if
377 successful, -1 if not. */
379 write_pid (const char *file
)
383 fp
= fopen (_PATH_UTMPDPID
, "w");
387 fprintf (fp
, "%d\n", getpid ());