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 /* 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
;
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;
213 return send_reply (connection
, &reply
.header
);
216 reply
.errnum
= errno
;
218 return send_reply (connection
, &reply
.header
);
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");
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)
246 /* Make sure we're in synch with the ordinary file. */
247 if (synchronize_database (connection
->database
) < 0)
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;
262 /* Update position pointer. */
263 connection
->position
++;
265 memcpy (&reply
.entry
, &connection
->last_entry
, sizeof (struct utmp
));
268 return send_reply (connection
, (reply_header
*)&reply
);
271 memset (&reply
.entry
, 0, sizeof (struct utmp
));
272 reply
.errnum
= errno
;
274 return send_reply (connection
, &reply
.header
);
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");
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
;
301 return send_reply (connection
, &reply
.header
);
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");
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)
329 /* Make sure we're in synch with the ordinary file. */
330 if (synchronize_database (connection
->database
) < 0)
338 /* Read the next entry. */
339 if (read_entry (connection
->database
, connection
->position
,
340 &connection
->last_entry
) < 0)
342 connection
->position
= -1;
346 connection
->position
++;
348 /* Stop if we found a user or login entry. */
350 #if _HAVE_UT_TYPE - 0
351 (connection
->last_entry
.ut_type
== USER_PROCESS
352 || connection
->last_entry
.ut_type
== LOGIN_PROCESS
)
355 !strncmp (request
->line
.ut_line
, connection
->last_entry
.ut_line
,
356 sizeof request
->line
.ut_line
))
360 memcpy (&reply
.entry
, &connection
->last_entry
, sizeof (struct utmp
));
363 return send_reply (connection
, &reply
.header
);
366 memset (&reply
.entry
, 0, sizeof (struct utmp
));
367 reply
.errnum
= errno
;
369 return send_reply (connection
, &reply
.header
);
374 do_getutid (client_connection
*connection
)
376 getutid_request
*request
;
379 request
= (getutid_request
*)connection
->read_base
;
380 if (request
->header
.size
!= sizeof (getutid_request
))
382 warning (EINVAL
, "invalid request size");
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)
397 /* Make sure we're in synch with the ordinary file. */
398 if (synchronize_database (connection
->database
) < 0)
404 if (internal_getut_r (connection
, &request
->id
,
405 &connection
->last_entry
) < 0)
413 memcpy (&reply
.entry
, &connection
->last_entry
, sizeof (struct utmp
));
414 return send_reply (connection
, &reply
.header
);
417 memset (&reply
.entry
, 0, sizeof (struct utmp
));
418 reply
.errnum
= errno
;
420 return send_reply (connection
, &reply
.header
);
425 do_pututline (client_connection
*connection
)
427 pututline_request
*request
;
428 pututline_reply reply
;
432 request
= (pututline_request
*)connection
->read_base
;
433 if (request
->header
.size
!= sizeof (pututline_request
))
435 warning (EINVAL
, "invalid request size");
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
))
450 if (connection
->database
== NULL
)
456 /* Make sure we're in synch with the ordinary file. */
457 if (synchronize_database (connection
->database
) < 0)
463 /* Find the correct place to insert the data. */
464 if (connection
->position
> 0
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
))
474 proc_utmp_eq (&connection
->last_entry
, &request
->utmp
)))
477 found
= internal_getut_r (connection
, &request
->utmp
, &buffer
);
481 /* We append the next entry. */
482 connection
->position
=
483 append_entry (connection
->database
, &request
->utmp
);
484 if (connection
->position
< 0)
489 /* We replace the just read entry. */
490 connection
->position
--;
491 if (write_entry (connection
->database
, connection
->position
,
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
++;
504 return send_reply (connection
, &reply
.header
);
507 reply
.errnum
= errno
;
509 return send_reply (connection
, &reply
.header
);
514 do_updwtmp (client_connection
*connection
)
516 updwtmp_request
*request
;
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
))
534 /* Select database. */
535 if (!strncmp (request
->file
, _PATH_UTMP
,
536 request
->header
.size
- sizeof (updwtmp_request
)))
544 /* Make sure we're in synch with the ordinary file. */
545 if (synchronize_database (database
) < 0)
551 /* Append the entry. */
552 if (append_entry (database
, &request
->utmp
) < 0)
557 return send_reply (connection
, &reply
.header
);
560 reply
.errnum
= errno
;
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
)
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
)
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)
589 strncmp (entry
->ut_line
, match
->ut_line
, sizeof match
->ut_line
) == 0
595 /* This function is derived from the one in login/utmp_file.c. */
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. */
609 /* Read the next entry. */
610 if (read_entry (connection
->database
, connection
->position
,
613 connection
->position
= -1;
616 connection
->position
++;
618 if (id
->ut_type
== buffer
->ut_type
)
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. */
630 /* Read the next entry. */
631 if (read_entry (connection
->database
, connection
->position
,
634 connection
->position
= -1;
637 connection
->position
++;
639 if (proc_utmp_eq (buffer
, id
))