Update.
[glibc.git] / login / programs / request.c
blob889ce0cba9fe9264c02d43b80aa008399dd75ba5
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 <assert.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <utmp.h>
26 #include "utmpd.h"
27 #include "utmpd-private.h"
30 /* Prototypes for the local functions. */
31 static int process_request (client_connection *connection);
32 static int send_reply (client_connection *connect, const reply_header *reply);
34 static int do_setutent (client_connection *connection);
35 static int do_getutent (client_connection *connection);
36 static int do_endutent (client_connection *connection);
37 static int do_getutline (client_connection *connection);
38 static int do_getutid (client_connection *connection);
39 static int do_pututline (client_connection *connection);
40 static int do_updwtmp (client_connection *connection);
42 static int internal_getut_r (client_connection *connection,
43 const struct utmp *id, struct utmp *buffer);
46 /* Read data from the client on CONNECTION. */
47 int
48 read_data (client_connection *connection)
50 ssize_t nbytes;
52 assert (connection);
53 assert ((connection->read_end - connection->read_ptr) > 0);
55 /* Read data. */
56 nbytes = read (connection->sock, connection->read_ptr,
57 connection->read_end - connection->read_ptr);
58 if (nbytes > 0)
60 size_t total_bytes;
62 /* Update read pointer. */
63 connection->read_ptr += nbytes;
65 /* Check if we have a complete request header. */
66 total_bytes = connection->read_ptr - connection->read_base;
67 if (total_bytes >= sizeof (request_header))
69 request_header *header;
71 /* Check if we have a complete request. */
72 header = (request_header *)connection->read_base;
73 if (total_bytes >= header->size)
75 /* Process the request. */
76 if (process_request (connection) < 0)
77 return -1;
79 /* Adjust read pointer, and flush buffer. */
80 connection->read_ptr -= header->size;
81 memmove (connection->read_base,
82 connection->read_base + header->size,
83 connection->read_ptr - connection->read_base);
87 return 0;
90 if (nbytes < 0)
91 error (0, errno, _("cannot read from client"));
93 return -1;
97 /* Write data to the client on CONNECTION. */
98 int
99 write_data (client_connection *connection)
101 ssize_t nbytes;
103 assert (connection);
104 assert ((connection->write_ptr - connection->write_base) > 0);
106 /* Write data. */
107 nbytes = write (connection->sock, connection->write_base,
108 connection->write_ptr - connection->write_base);
109 if (nbytes > 0)
111 /* Adjust write pointer and flush buffer. */
112 connection->write_ptr -= nbytes;
113 memmove (connection->write_base, connection->write_base + nbytes,
114 connection->write_ptr - connection->write_base);
116 return 0;
119 if (nbytes < 0)
120 error (0, errno, _("cannot write to client"));
122 return -1;
126 /* Process the request received on CONNECTION. Returns 0 if
127 successful, -1 if not. */
128 static int
129 process_request (client_connection *connection)
131 request_header *header;
133 assert (connection);
134 assert (connection->read_base);
136 header = (request_header *)connection->read_base;
137 if (header->version != UTMPD_VERSION)
139 warning (EINVAL, "invalid protocol version");
140 return -1;
143 switch (header->type)
145 case UTMPD_REQ_SETUTENT: return do_setutent (connection);
146 case UTMPD_REQ_GETUTENT: return do_getutent (connection);
147 case UTMPD_REQ_ENDUTENT: return do_endutent (connection);
148 case UTMPD_REQ_GETUTLINE: return do_getutline (connection);
149 case UTMPD_REQ_GETUTID: return do_getutid (connection);
150 case UTMPD_REQ_PUTUTLINE: return do_pututline (connection);
151 case UTMPD_REQ_UPDWTMP: return do_updwtmp (connection);
152 default:
153 warning (EINVAL, "invalid request type");
154 return -1;
159 /* Send the reply specified by HEADER to the client on CONNECTION.
160 Returns 0 if successful, -1 if not. */
161 static int
162 send_reply (client_connection *connection, const reply_header *reply)
164 /* Check if the reply fits in the buffer. */
165 if ((size_t) (connection->write_end - connection->write_ptr) < reply->size)
167 error (0, 0, _("buffer overflow"));
168 return -1;
171 /* Copy reply to buffer, and adjust write pointer. */
172 memcpy (connection->write_ptr, reply, reply->size);
173 connection->write_ptr += reply->size;
175 return 0;
179 static int
180 do_setutent (client_connection *connection)
182 setutent_request *request;
183 setutent_reply reply;
185 /* The request size varies, so don't check it. */
186 request = (setutent_request *)connection->read_base;
188 /* Initialize reply. */
189 reply.header.version = UTMPD_VERSION;
190 reply.header.size = sizeof (setutent_reply);
191 reply.header.type = UTMPD_REQ_SETUTENT;
193 /* Select database. */
194 if (!strncmp (request->file, _PATH_UTMP,
195 request->header.size - sizeof (setutent_request)))
196 connection->database = utmp_db;
197 else
199 errno = EINVAL;
200 goto return_error;
203 /* Initialize position pointer. */
204 connection->position = 0;
206 #if _HAVE_UT_TYPE - 0
207 /* Make sure the entry won't match. */
208 connection->last_entry.ut_type = -1;
209 #endif
211 reply.errnum = 0;
212 reply.result = 0;
213 return send_reply (connection, &reply.header);
215 return_error:
216 reply.errnum = errno;
217 reply.result = -1;
218 return send_reply (connection, &reply.header);
222 static int
223 do_getutent (client_connection *connection)
225 getutent_request *request;
226 getutent_reply reply;
228 request = (getutent_request *)connection->read_base;
229 if (request->header.size != sizeof (getutent_request))
231 warning (EINVAL, "invalid request size");
232 return -1;
235 /* Initialize reply. */
236 reply.header.version = UTMPD_VERSION;
237 reply.header.size = sizeof (getutent_reply);
238 reply.header.type = UTMPD_REQ_GETUTENT;
240 if (connection->database == NULL || connection->position == -1)
242 errno = ESRCH;
243 goto return_error;
246 /* Make sure we're in synch with the ordinary file. */
247 if (synchronize_database (connection->database) < 0)
249 errno = ESRCH;
250 goto return_error;
253 /* Read the next entry from the database. */
254 if (read_entry (connection->database, connection->position,
255 &connection->last_entry) < 0)
257 connection->position = -1;
258 errno = ESRCH;
259 goto return_error;
262 /* Update position pointer. */
263 connection->position++;
265 memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
266 reply.errnum = 0;
267 reply.result = 0;
268 return send_reply (connection, (reply_header *)&reply);
270 return_error:
271 memset (&reply.entry, 0, sizeof (struct utmp));
272 reply.errnum = errno;
273 reply.result = -1;
274 return send_reply (connection, &reply.header);
278 static int
279 do_endutent (client_connection *connection)
281 endutent_request *request;
282 endutent_reply reply;
284 request = (endutent_request *)connection->read_base;
285 if (request->header.size != sizeof (endutent_request))
287 warning (EINVAL, "invalid request size");
288 return -1;
291 /* Deselect database. */
292 connection->database = NULL;
294 /* Formulate reply. */
295 reply.header.version = UTMPD_VERSION;
296 reply.header.size = sizeof (endutent_reply);
297 reply.header.type = UTMPD_REQ_ENDUTENT;
298 reply.errnum = 0;
299 reply.result = 0;
301 return send_reply (connection, &reply.header);
305 static int
306 do_getutline (client_connection *connection)
308 getutline_request *request;
309 getutline_reply reply;
311 request = (getutline_request *)connection->read_base;
312 if (request->header.size != sizeof (getutline_request))
314 warning (EINVAL, "invalid request size");
315 return -1;
318 /* Initialize reply. */
319 reply.header.version = UTMPD_VERSION;
320 reply.header.size = sizeof (getutline_reply);
321 reply.header.type = UTMPD_REQ_GETUTLINE;
323 if (connection->database == NULL || connection->position == -1)
325 errno = ESRCH;
326 goto return_error;
329 /* Make sure we're in synch with the ordinary file. */
330 if (synchronize_database (connection->database) < 0)
332 errno = ESRCH;
333 goto return_error;
336 while (1)
338 /* Read the next entry. */
339 if (read_entry (connection->database, connection->position,
340 &connection->last_entry) < 0)
342 connection->position = -1;
343 errno = ESRCH;
344 goto return_error;
346 connection->position++;
348 /* Stop if we found a user or login entry. */
349 if (
350 #if _HAVE_UT_TYPE - 0
351 (connection->last_entry.ut_type == USER_PROCESS
352 || connection->last_entry.ut_type == LOGIN_PROCESS)
354 #endif
355 !strncmp (request->line.ut_line, connection->last_entry.ut_line,
356 sizeof request->line.ut_line))
357 break;
360 memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
361 reply.errnum = 0;
362 reply.result = 0;
363 return send_reply (connection, &reply.header);
365 return_error:
366 memset (&reply.entry, 0, sizeof (struct utmp));
367 reply.errnum = errno;
368 reply.result = -1;
369 return send_reply (connection, &reply.header);
373 static int
374 do_getutid (client_connection *connection)
376 getutid_request *request;
377 getutid_reply reply;
379 request = (getutid_request *)connection->read_base;
380 if (request->header.size != sizeof (getutid_request))
382 warning (EINVAL, "invalid request size");
383 return -1;
386 /* Initialize reply. */
387 reply.header.version = UTMPD_VERSION;
388 reply.header.size = sizeof (getutid_reply);
389 reply.header.type = UTMPD_REQ_GETUTID;
391 if (connection->database == NULL || connection->position == -1)
393 errno = ESRCH;
394 goto return_error;
397 /* Make sure we're in synch with the ordinary file. */
398 if (synchronize_database (connection->database) < 0)
400 errno = ESRCH;
401 goto return_error;
404 if (internal_getut_r (connection, &request->id,
405 &connection->last_entry) < 0)
407 errno = ESRCH;
408 goto return_error;
411 reply.errnum = 0;
412 reply.result = 0;
413 memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
414 return send_reply (connection, &reply.header);
416 return_error:
417 memset (&reply.entry, 0, sizeof (struct utmp));
418 reply.errnum = errno;
419 reply.result = -1;
420 return send_reply (connection, &reply.header);
424 static int
425 do_pututline (client_connection *connection)
427 pututline_request *request;
428 pututline_reply reply;
429 struct utmp buffer;
430 int found;
432 request = (pututline_request *)connection->read_base;
433 if (request->header.size != sizeof (pututline_request))
435 warning (EINVAL, "invalid request size");
436 return -1;
439 /* Initialize reply. */
440 reply.header.version = UTMPD_VERSION;
441 reply.header.size = sizeof (pututline_reply);
442 reply.header.type = UTMPD_REQ_PUTUTLINE;
444 if (!(connection->access & W_OK))
446 errno = EPERM;
447 goto return_error;
450 if (connection->database == NULL)
452 errno = ESRCH;
453 goto return_error;
456 /* Make sure we're in synch with the ordinary file. */
457 if (synchronize_database (connection->database) < 0)
459 errno = ESRCH;
460 goto return_error;
463 /* Find the correct place to insert the data. */
464 if (connection->position > 0
465 && (
466 #if _HAVE_UT_TYPE - 0
467 (connection->last_entry.ut_type == request->utmp.ut_type
468 && (connection->last_entry.ut_type == RUN_LVL
469 || connection->last_entry.ut_type == BOOT_TIME
470 || connection->last_entry.ut_type == OLD_TIME
471 || connection->last_entry.ut_type == NEW_TIME))
473 #endif
474 proc_utmp_eq (&connection->last_entry, &request->utmp)))
475 found = 1;
476 else
477 found = internal_getut_r (connection, &request->utmp, &buffer);
479 if (found < 0)
481 /* We append the next entry. */
482 connection->position =
483 append_entry (connection->database, &request->utmp);
484 if (connection->position < 0)
485 goto return_error;
487 else
489 /* We replace the just read entry. */
490 connection->position--;
491 if (write_entry (connection->database, connection->position,
492 &request->utmp) < 0)
493 goto return_error;
496 /* Write the entry to the compatibility file. */
497 write_old_entry (connection->database, connection->position, &request->utmp);
499 /* Update position pointer. */
500 connection->position++;
502 reply.errnum = 0;
503 reply.result = 0;
504 return send_reply (connection, &reply.header);
506 return_error:
507 reply.errnum = errno;
508 reply.result = -1;
509 return send_reply (connection, &reply.header);
513 static int
514 do_updwtmp (client_connection *connection)
516 updwtmp_request *request;
517 updwtmp_reply reply;
518 utmp_database *database;
520 /* The request size varies, so don't check it. */
521 request = (updwtmp_request *)connection->read_base;
523 /* Initialize reply. */
524 reply.header.version = UTMPD_VERSION;
525 reply.header.size = sizeof (updwtmp_reply);
526 reply.header.type = UTMPD_REQ_UPDWTMP;
528 if (!(connection->access & W_OK))
530 errno = EPERM;
531 goto return_error;
534 /* Select database. */
535 if (!strncmp (request->file, _PATH_UTMP,
536 request->header.size - sizeof (updwtmp_request)))
537 database = utmp_db;
538 else
540 errno = EINVAL;
541 goto return_error;
544 /* Make sure we're in synch with the ordinary file. */
545 if (synchronize_database (database) < 0)
547 errno = ESRCH;
548 goto return_error;
551 /* Append the entry. */
552 if (append_entry (database, &request->utmp) < 0)
553 goto return_error;
555 reply.errnum = 0;
556 reply.result = 0;
557 return send_reply (connection, &reply.header);
559 return_error:
560 reply.errnum = errno;
561 reply.result = -1;
562 return send_reply (connection, &reply.header);
566 /* This function is identical to the one in login/utmp_file.c. */
568 proc_utmp_eq (const struct utmp *entry, const struct utmp *match)
570 return
572 #if _HAVE_UT_TYPE - 0
573 (entry->ut_type == INIT_PROCESS
574 || entry->ut_type == LOGIN_PROCESS
575 || entry->ut_type == USER_PROCESS
576 || entry->ut_type == DEAD_PROCESS)
578 (match->ut_type == INIT_PROCESS
579 || match->ut_type == LOGIN_PROCESS
580 || match->ut_type == USER_PROCESS
581 || match->ut_type == DEAD_PROCESS)
583 #endif
584 #if _HAVE_UT_ID - 0
585 (entry->ut_id[0] && match->ut_id[0]
586 ? strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
587 : strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0)
588 #else
589 strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0
590 #endif
595 /* This function is derived from the one in login/utmp_file.c. */
596 static int
597 internal_getut_r (client_connection *connection,
598 const struct utmp *id, struct utmp *buffer)
600 #if _HAVE_UT_TYPE - 0
601 if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
602 || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
604 /* Search for next entry with type RUN_LVL, BOOT_TIME,
605 OLD_TIME, or NEW_TIME. */
607 while (1)
609 /* Read the next entry. */
610 if (read_entry (connection->database, connection->position,
611 buffer) < 0)
613 connection->position = -1;
614 return -1;
616 connection->position++;
618 if (id->ut_type == buffer->ut_type)
619 break;
622 else
623 #endif /* _HAVE_UT_TYPE */
625 /* Search for the next entry with the specified ID and with type
626 INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS. */
628 while (1)
630 /* Read the next entry. */
631 if (read_entry (connection->database, connection->position,
632 buffer) < 0)
634 connection->position = -1;
635 return -1;
637 connection->position++;
639 if (proc_utmp_eq (buffer, id))
640 break;
644 return 0;