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. */
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. */
48 read_data (client_connection
*connection
)
53 assert ((connection
->read_end
- connection
->read_ptr
) > 0);
56 nbytes
= read (connection
->sock
, connection
->read_ptr
,
57 connection
->read_end
- connection
->read_ptr
);
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)
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
);
91 error (0, errno
, "cannot read from client");
97 /* Write data to the client on CONNECTION. */
99 write_data (client_connection
*connection
)
104 assert ((connection
->write_ptr
- connection
->write_base
) > 0);
107 nbytes
= write (connection
->sock
, connection
->write_base
,
108 connection
->write_ptr
- connection
->write_base
);
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
);
120 error (0, errno
, "cannot write to client");
126 /* Process the request received on CONNECTION. Returns 0 if
127 successful, -1 if not. */
129 process_request (client_connection
*connection
)
131 request_header
*header
;
134 assert (connection
->read_base
);
136 header
= (request_header
*)connection
->read_base
;
137 if (header
->version
!= UTMPD_VERSION
)
139 warning (EINVAL
, "invalid protocol version");
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
);
153 warning (EINVAL
, "invalid request type");
159 /* Send the reply specified by HEADER to the client on CONNECTION.
160 Returns 0 if successful, -1 if not. */
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");
171 /* Copy reply to buffer, and adjust write pointer. */
172 memcpy (connection
->write_ptr
, reply
, reply
->size
);
173 connection
->write_ptr
+= reply
->size
;
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");
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
;
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;
216 return send_reply (connection
, &reply
.header
);
219 reply
.errnum
= errno
;
221 return send_reply (connection
, &reply
.header
);
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");
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)
249 /* Make sure we're in synch with the ordinary file. */
250 if (synchronize_database (connection
->database
) < 0)
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;
265 /* Update position pointer. */
266 connection
->position
++;
268 memcpy (&reply
.entry
, &connection
->last_entry
, sizeof (struct utmp
));
271 return send_reply (connection
, (reply_header
*)&reply
);
274 memset (&reply
.entry
, 0, sizeof (struct utmp
));
275 reply
.errnum
= errno
;
277 return send_reply (connection
, &reply
.header
);
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");
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
;
304 return send_reply (connection
, &reply
.header
);
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");
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)
332 /* Make sure we're in synch with the ordinary file. */
333 if (synchronize_database (connection
->database
) < 0)
341 /* Read the next entry. */
342 if (read_entry (connection
->database
, connection
->position
,
343 &connection
->last_entry
) < 0)
345 connection
->position
= -1;
349 connection
->position
++;
351 /* Stop if we found a user or login entry. */
353 #if _HAVE_UT_TYPE - 0
354 (connection
->last_entry
.ut_type
== USER_PROCESS
355 || connection
->last_entry
.ut_type
== LOGIN_PROCESS
)
358 !strncmp (request
->line
.ut_line
, connection
->last_entry
.ut_line
,
359 sizeof request
->line
.ut_line
))
363 memcpy (&reply
.entry
, &connection
->last_entry
, sizeof (struct utmp
));
366 return send_reply (connection
, &reply
.header
);
369 memset (&reply
.entry
, 0, sizeof (struct utmp
));
370 reply
.errnum
= errno
;
372 return send_reply (connection
, &reply
.header
);
377 do_getutid (client_connection
*connection
)
379 getutid_request
*request
;
382 request
= (getutid_request
*)connection
->read_base
;
383 if (request
->header
.size
!= sizeof (getutid_request
))
385 warning (EINVAL
, "invalid request size");
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)
400 /* Make sure we're in synch with the ordinary file. */
401 if (synchronize_database (connection
->database
) < 0)
407 if (internal_getut_r (connection
, &request
->id
,
408 &connection
->last_entry
) < 0)
416 memcpy (&reply
.entry
, &connection
->last_entry
, sizeof (struct utmp
));
417 return send_reply (connection
, &reply
.header
);
420 memset (&reply
.entry
, 0, sizeof (struct utmp
));
421 reply
.errnum
= errno
;
423 return send_reply (connection
, &reply
.header
);
428 do_pututline (client_connection
*connection
)
430 pututline_request
*request
;
431 pututline_reply reply
;
435 request
= (pututline_request
*)connection
->read_base
;
436 if (request
->header
.size
!= sizeof (pututline_request
))
438 warning (EINVAL
, "invalid request size");
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
))
453 if (connection
->database
== NULL
|| connection
->position
== -1)
459 /* Make sure we're in synch with the ordinary file. */
460 if (synchronize_database (connection
->database
) < 0)
466 /* Find the correct place to insert the data. */
467 if (connection
->position
> 0
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
))
477 proc_utmp_eq (&connection
->last_entry
, &request
->utmp
)))
480 found
= internal_getut_r (connection
, &request
->utmp
, &buffer
);
484 /* We append the next entry. */
485 connection
->position
=
486 append_entry (connection
->database
, &request
->utmp
);
487 if (connection
->position
< 0)
492 /* We replace the just read entry. */
493 connection
->position
--;
494 if (write_entry (connection
->database
, connection
->position
,
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
++;
507 return send_reply (connection
, &reply
.header
);
510 reply
.errnum
= errno
;
512 return send_reply (connection
, &reply
.header
);
517 do_updwtmp (client_connection
*connection
)
519 updwtmp_request
*request
;
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");
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
))
541 /* Select database. */
542 if (!strncmp (request
->file
, _PATH_UTMP
, sizeof request
->file
))
550 /* Make sure we're in synch with the ordinary file. */
551 if (synchronize_database (database
) < 0)
557 /* Append the entry. */
558 if (append_entry (database
, &request
->utmp
) < 0)
563 return send_reply (connection
, &reply
.header
);
566 reply
.errnum
= errno
;
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
)
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
)
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)
595 strncmp (entry
->ut_line
, match
->ut_line
, sizeof match
->ut_line
) == 0
601 /* This function is derived from the one in login/utmp_file.c. */
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. */
615 /* Read the next entry. */
616 if (read_entry (connection
->database
, connection
->position
,
619 connection
->position
= -1;
622 connection
->position
++;
624 if (id
->ut_type
== buffer
->ut_type
)
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. */
636 /* Read the next entry. */
637 if (read_entry (connection
->database
, connection
->position
,
640 connection
->position
= -1;
643 connection
->position
++;
645 if (proc_utmp_eq (buffer
, id
))