Update.
[glibc.git] / login / programs / request.c
blobd2c12e68cf05c520dae6fdca8db1cbfb0d4994b0
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 request = (setutent_request *)connection->read_base;
186 if (request->header.size != sizeof (setutent_request))
188 warning (EINVAL, "invalid request size");
189 return -1;
192 /* Initialize reply. */
193 reply.header.version = UTMPD_VERSION;
194 reply.header.size = sizeof (setutent_reply);
195 reply.header.type = UTMPD_REQ_SETUTENT;
197 /* Select database. */
198 if (!strncmp (request->file, _PATH_UTMP, sizeof request->file))
199 connection->database = utmp_db;
200 else
202 errno = EINVAL;
203 goto return_error;
206 /* Initialize position pointer. */
207 connection->position = 0;
209 #if _HAVE_UT_TYPE - 0
210 /* Make sure the entry won't match. */
211 connection->last_entry.ut_type = -1;
212 #endif
214 reply.errnum = 0;
215 reply.result = 0;
216 return send_reply (connection, &reply.header);
218 return_error:
219 reply.errnum = errno;
220 reply.result = -1;
221 return send_reply (connection, &reply.header);
225 static int
226 do_getutent (client_connection *connection)
228 getutent_request *request;
229 getutent_reply reply;
231 request = (getutent_request *)connection->read_base;
232 if (request->header.size != sizeof (getutent_request))
234 warning (EINVAL, "invalid request size");
235 return -1;
238 /* Initialize reply. */
239 reply.header.version = UTMPD_VERSION;
240 reply.header.size = sizeof (getutent_reply);
241 reply.header.type = UTMPD_REQ_GETUTENT;
243 if (connection->database == NULL || connection->position == -1)
245 errno = ESRCH;
246 goto return_error;
249 /* Make sure we're in synch with the ordinary file. */
250 if (synchronize_database (connection->database) < 0)
252 errno = ESRCH;
253 goto return_error;
256 /* Read the next entry from the database. */
257 if (read_entry (connection->database, connection->position,
258 &connection->last_entry) < 0)
260 connection->position = -1;
261 errno = ESRCH;
262 goto return_error;
265 /* Update position pointer. */
266 connection->position++;
268 memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
269 reply.errnum = 0;
270 reply.result = 0;
271 return send_reply (connection, (reply_header *)&reply);
273 return_error:
274 memset (&reply.entry, 0, sizeof (struct utmp));
275 reply.errnum = errno;
276 reply.result = -1;
277 return send_reply (connection, &reply.header);
281 static int
282 do_endutent (client_connection *connection)
284 endutent_request *request;
285 endutent_reply reply;
287 request = (endutent_request *)connection->read_base;
288 if (request->header.size != sizeof (endutent_request))
290 warning (EINVAL, "invalid request size");
291 return -1;
294 /* Deselect database. */
295 connection->database = NULL;
297 /* Formulate reply. */
298 reply.header.version = UTMPD_VERSION;
299 reply.header.size = sizeof (endutent_reply);
300 reply.header.type = UTMPD_REQ_ENDUTENT;
301 reply.errnum = 0;
302 reply.result = 0;
304 return send_reply (connection, &reply.header);
308 static int
309 do_getutline (client_connection *connection)
311 getutline_request *request;
312 getutline_reply reply;
314 request = (getutline_request *)connection->read_base;
315 if (request->header.size != sizeof (getutline_request))
317 warning (EINVAL, "invalid request size");
318 return -1;
321 /* Initialize reply. */
322 reply.header.version = UTMPD_VERSION;
323 reply.header.size = sizeof (getutline_reply);
324 reply.header.type = UTMPD_REQ_GETUTLINE;
326 if (connection->database == NULL || connection->position == -1)
328 errno = ESRCH;
329 goto return_error;
332 /* Make sure we're in synch with the ordinary file. */
333 if (synchronize_database (connection->database) < 0)
335 errno = ESRCH;
336 goto return_error;
339 while (1)
341 /* Read the next entry. */
342 if (read_entry (connection->database, connection->position,
343 &connection->last_entry) < 0)
345 connection->position = -1;
346 errno = ESRCH;
347 goto return_error;
349 connection->position++;
351 /* Stop if we found a user or login entry. */
352 if (
353 #if _HAVE_UT_TYPE - 0
354 (connection->last_entry.ut_type == USER_PROCESS
355 || connection->last_entry.ut_type == LOGIN_PROCESS)
357 #endif
358 !strncmp (request->line.ut_line, connection->last_entry.ut_line,
359 sizeof request->line.ut_line))
360 break;
363 memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
364 reply.errnum = 0;
365 reply.result = 0;
366 return send_reply (connection, &reply.header);
368 return_error:
369 memset (&reply.entry, 0, sizeof (struct utmp));
370 reply.errnum = errno;
371 reply.result = -1;
372 return send_reply (connection, &reply.header);
376 static int
377 do_getutid (client_connection *connection)
379 getutid_request *request;
380 getutid_reply reply;
382 request = (getutid_request *)connection->read_base;
383 if (request->header.size != sizeof (getutid_request))
385 warning (EINVAL, "invalid request size");
386 return -1;
389 /* Initialize reply. */
390 reply.header.version = UTMPD_VERSION;
391 reply.header.size = sizeof (getutid_reply);
392 reply.header.type = UTMPD_REQ_GETUTID;
394 if (connection->database == NULL || connection->position == -1)
396 errno = ESRCH;
397 goto return_error;
400 /* Make sure we're in synch with the ordinary file. */
401 if (synchronize_database (connection->database) < 0)
403 errno = ESRCH;
404 goto return_error;
407 if (internal_getut_r (connection, &request->id,
408 &connection->last_entry) < 0)
410 errno = ESRCH;
411 goto return_error;
414 reply.errnum = 0;
415 reply.result = 0;
416 memcpy (&reply.entry, &connection->last_entry, sizeof (struct utmp));
417 return send_reply (connection, &reply.header);
419 return_error:
420 memset (&reply.entry, 0, sizeof (struct utmp));
421 reply.errnum = errno;
422 reply.result = -1;
423 return send_reply (connection, &reply.header);
427 static int
428 do_pututline (client_connection *connection)
430 pututline_request *request;
431 pututline_reply reply;
432 struct utmp buffer;
433 int found;
435 request = (pututline_request *)connection->read_base;
436 if (request->header.size != sizeof (pututline_request))
438 warning (EINVAL, "invalid request size");
439 return -1;
442 /* Initialize reply. */
443 reply.header.version = UTMPD_VERSION;
444 reply.header.size = sizeof (pututline_reply);
445 reply.header.type = UTMPD_REQ_PUTUTLINE;
447 if (!(connection->access & W_OK))
449 errno = EPERM;
450 goto return_error;
453 if (connection->database == NULL || connection->position == -1)
455 errno = ESRCH;
456 goto return_error;
459 /* Make sure we're in synch with the ordinary file. */
460 if (synchronize_database (connection->database) < 0)
462 errno = ESRCH;
463 goto return_error;
466 /* Find the correct place to insert the data. */
467 if (connection->position > 0
468 && (
469 #if _HAVE_UT_TYPE - 0
470 (connection->last_entry.ut_type == request->utmp.ut_type
471 && (connection->last_entry.ut_type == RUN_LVL
472 || connection->last_entry.ut_type == BOOT_TIME
473 || connection->last_entry.ut_type == OLD_TIME
474 || connection->last_entry.ut_type == NEW_TIME))
476 #endif
477 proc_utmp_eq (&connection->last_entry, &request->utmp)))
478 found = 1;
479 else
480 found = internal_getut_r (connection, &request->utmp, &buffer);
482 if (found < 0)
484 /* We append the next entry. */
485 connection->position =
486 append_entry (connection->database, &request->utmp);
487 if (connection->position < 0)
488 goto return_error;
490 else
492 /* We replace the just read entry. */
493 connection->position--;
494 if (write_entry (connection->database, connection->position,
495 &request->utmp) < 0)
496 goto return_error;
499 /* Write the entry to the compatibility file. */
500 write_old_entry (connection->database, connection->position, &request->utmp);
502 /* Update position pointer. */
503 connection->position++;
505 reply.errnum = 0;
506 reply.result = 0;
507 return send_reply (connection, &reply.header);
509 return_error:
510 reply.errnum = errno;
511 reply.result = -1;
512 return send_reply (connection, &reply.header);
516 static int
517 do_updwtmp (client_connection *connection)
519 updwtmp_request *request;
520 updwtmp_reply reply;
521 utmp_database *database;
523 request = (updwtmp_request *)connection->read_base;
524 if (request->header.size != sizeof (updwtmp_request))
526 warning (EINVAL, "invalid request size");
527 return -1;
530 /* Initialize reply. */
531 reply.header.version = UTMPD_VERSION;
532 reply.header.size = sizeof (updwtmp_reply);
533 reply.header.type = UTMPD_REQ_UPDWTMP;
535 if (!(connection->access & W_OK))
537 errno = EPERM;
538 goto return_error;
541 /* Select database. */
542 if (!strncmp (request->file, _PATH_UTMP, sizeof request->file))
543 database = utmp_db;
544 else
546 errno = EINVAL;
547 goto return_error;
550 /* Make sure we're in synch with the ordinary file. */
551 if (synchronize_database (database) < 0)
553 errno = ESRCH;
554 goto return_error;
557 /* Append the entry. */
558 if (append_entry (database, &request->utmp) < 0)
559 goto return_error;
561 reply.errnum = 0;
562 reply.result = 0;
563 return send_reply (connection, &reply.header);
565 return_error:
566 reply.errnum = errno;
567 reply.result = -1;
568 return send_reply (connection, &reply.header);
572 /* This function is identical to the one in login/utmp_file.c. */
574 proc_utmp_eq (const struct utmp *entry, const struct utmp *match)
576 return
578 #if _HAVE_UT_TYPE - 0
579 (entry->ut_type == INIT_PROCESS
580 || entry->ut_type == LOGIN_PROCESS
581 || entry->ut_type == USER_PROCESS
582 || entry->ut_type == DEAD_PROCESS)
584 (match->ut_type == INIT_PROCESS
585 || match->ut_type == LOGIN_PROCESS
586 || match->ut_type == USER_PROCESS
587 || match->ut_type == DEAD_PROCESS)
589 #endif
590 #if _HAVE_UT_ID - 0
591 (entry->ut_id[0] && match->ut_id[0]
592 ? strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
593 : strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0)
594 #else
595 strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0
596 #endif
601 /* This function is derived from the one in login/utmp_file.c. */
602 static int
603 internal_getut_r (client_connection *connection,
604 const struct utmp *id, struct utmp *buffer)
606 #if _HAVE_UT_TYPE - 0
607 if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
608 || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
610 /* Search for next entry with type RUN_LVL, BOOT_TIME,
611 OLD_TIME, or NEW_TIME. */
613 while (1)
615 /* Read the next entry. */
616 if (read_entry (connection->database, connection->position,
617 buffer) < 0)
619 connection->position = -1;
620 return -1;
622 connection->position++;
624 if (id->ut_type == buffer->ut_type)
625 break;
628 else
629 #endif /* _HAVE_UT_TYPE */
631 /* Search for the next entry with the specified ID and with type
632 INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS. */
634 while (1)
636 /* Read the next entry. */
637 if (read_entry (connection->database, connection->position,
638 buffer) < 0)
640 connection->position = -1;
641 return -1;
643 connection->position++;
645 if (proc_utmp_eq (buffer, id))
646 break;
650 return 0;