Update.
[kopensolaris-gnu/glibc.git] / login / utmp_daemon.c
1 /* Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
4
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.
9
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.
14
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.  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <unistd.h>
28 #include <utmp.h>
29
30 #include "utmp-private.h"
31 #include "programs/utmpd.h"
32
33
34 /* Descriptor for the socket.  */
35 static int daemon_sock = -1;
36
37
38 /* Functions defined here.  */
39 static int setutent_daemon (void);
40 static int getutent_r_daemon (struct utmp *buffer, struct utmp **result);
41 static int getutid_r_daemon (const struct utmp *line, struct utmp *buffer,
42                              struct utmp **result);
43 static int getutline_r_daemon (const struct utmp *id, struct utmp *buffer,
44                                struct utmp **result);
45 static struct utmp *pututline_daemon (const struct utmp *utmp);
46 static void endutent_daemon (void);
47 static int updwtmp_daemon (const char *file, const struct utmp *utmp);
48
49 /* Jump table for daemon functions.  */
50 struct utfuncs __libc_utmp_daemon_functions =
51 {
52   setutent_daemon,
53   getutent_r_daemon,
54   getutid_r_daemon,
55   getutline_r_daemon,
56   pututline_daemon,
57   endutent_daemon,
58   updwtmp_daemon
59 };
60
61 static int do_setutent (int sock);
62 static int do_getutent (int sock, struct utmp *buffer);
63 static int do_getutid (int sock, const struct utmp *id,
64                             struct utmp *buffer);
65 static int do_pututline (int sock, const struct utmp *utmp);
66 static int do_getutline (int sock, const struct utmp *line,
67                          struct utmp *buffer);
68 static int do_pututline (int sock, const struct utmp *utmp);
69 static int do_endutent (int sock);
70 static int do_updwtmp (int sock, const char *file,
71                        const struct utmp *utmp);
72
73 static int open_socket (const char *name);
74 static int send_request (int sock, const request_header *request,
75                          reply_header *reply);
76
77
78 static int
79 setutent_daemon (void)
80 {
81   if (__access (_PATH_UTMPD_RW, F_OK) == -1
82       && __access (_PATH_UTMPD_RO, F_OK) == -1)
83     return 0;
84
85   if (daemon_sock < 0)
86     {
87       int result;
88
89       daemon_sock = open_socket (_PATH_UTMPD_RW);
90       if (daemon_sock < 0)
91         {
92           /* Hhm, read-write access did not work.  Try read-only.  */
93           daemon_sock = open_socket (_PATH_UTMPD_RO);
94           if (daemon_sock < 0)
95             return 0;
96         }
97
98       /* We have to make sure the socket is `closed on exec'.  */
99       result = __fcntl (daemon_sock, F_GETFD, 0);
100       if (result >= 0)
101         result = __fcntl (daemon_sock, F_SETFD, result | FD_CLOEXEC);
102       if (result == -1)
103         {
104           close (daemon_sock);
105           return 0;
106         }
107     }
108
109   /* Send request to the daemon.  */
110   if (do_setutent (daemon_sock) < 0)
111     return 0;
112
113   return 1;
114 }
115
116
117 static int
118 getutent_r_daemon (struct utmp *buffer, struct utmp **result)
119 {
120   assert (daemon_sock >= 0);
121
122   /* Send request to the daemon.  */
123   if (do_getutent (daemon_sock, buffer) < 0)
124     {
125       *result = NULL;
126       return -1;;
127     }
128
129   *result = buffer;
130   return 0;
131 }
132
133
134 static int
135 getutid_r_daemon (const struct utmp *id, struct utmp *buffer,
136                   struct utmp **result)
137 {
138   assert (daemon_sock >= 0);
139
140   /* Send request to the daemon.  */
141   if (do_getutid (daemon_sock, id, buffer) < 0)
142     {
143       *result = NULL;
144       return -1;
145     }
146
147   *result = buffer;
148   return 0;
149 }
150
151
152 static int
153 getutline_r_daemon (const struct utmp *line, struct utmp *buffer,
154                     struct utmp **result)
155 {
156   assert (daemon_sock >= 0);
157
158   /* Send request to the daemon.  */
159   if (do_getutline (daemon_sock, line, buffer) < 0)
160     {
161       *result = NULL;
162       return -1;
163     }
164
165   *result = buffer;
166   return 0;
167 }
168
169
170 static struct utmp *
171 pututline_daemon (const struct utmp *utmp)
172 {
173   assert (daemon_sock >= 0);
174
175   /* Send request to the daemon.  */
176   if (do_pututline (daemon_sock, utmp) < 0)
177     return NULL;
178
179   return (struct utmp *)utmp;
180 }
181
182
183 static void
184 endutent_daemon (void)
185 {
186   assert (daemon_sock >= 0);
187
188   /* Send request to the daemon.  */
189   do_endutent (daemon_sock);
190
191   __close (daemon_sock);
192   daemon_sock = -1;
193 }
194
195
196 static int
197 updwtmp_daemon (const char *file, const struct utmp *utmp)
198 {
199   int sock;
200
201   /* Only try to open for both reading and writing.  */
202   sock = open_socket (_PATH_UTMPD_RW);
203   if (sock < 0)
204     return -1;
205
206   /* Send request to the daemon.  */
207   if (do_updwtmp (sock, file, utmp) < 0)
208     {
209       __close (sock);
210       return -1;
211     }
212
213   __close (sock);
214   return 0;
215 }
216
217
218 static int
219 do_setutent (int sock)
220 {
221   setutent_request *request;
222   setutent_reply reply;
223   size_t size;
224   size_t name_len;
225
226   name_len = strlen (__libc_utmp_file_name) + 1;
227   size = sizeof (setutent_request) + name_len;
228
229   request = malloc (size);
230   if (request == NULL)
231     return -1;
232
233   request->header.version = UTMPD_VERSION;
234   request->header.size = size;
235   request->header.type = UTMPD_REQ_SETUTENT;
236   memcpy (request->file, __libc_utmp_file_name, name_len);
237
238   reply.header.version = UTMPD_VERSION;
239   reply.header.size = sizeof (setutent_reply);
240   reply.header.type = UTMPD_REQ_SETUTENT;
241
242   if (send_request (sock, &request->header, &reply.header) < 0)
243     {
244       free (request);
245       return -1;
246     }
247
248   if (reply.result < 0)
249     __set_errno (reply.errnum);
250
251   free (request);
252   return reply.result;
253 }
254
255 static int
256 do_getutent (int sock, struct utmp *buffer)
257 {
258   getutent_request request;
259   getutent_reply reply;
260
261   request.header.version = UTMPD_VERSION;
262   request.header.size = sizeof (getutent_request);
263   request.header.type = UTMPD_REQ_GETUTENT;
264
265   reply.header.version = UTMPD_VERSION;
266   reply.header.size = sizeof (getutent_reply);
267   reply.header.type = UTMPD_REQ_GETUTENT;
268
269   if (send_request (sock, &request.header, &reply.header) < 0)
270     return -1;
271
272   if (reply.result < 0)
273     __set_errno (reply.errnum);
274   else
275     memcpy (buffer, &reply.entry, sizeof (struct utmp));
276
277   return reply.result;
278 }
279
280 static int
281 do_getutid (int sock, const struct utmp *id, struct utmp *buffer)
282 {
283   getutid_request request;
284   getutid_reply reply;
285
286   request.header.version = UTMPD_VERSION;
287   request.header.size = sizeof (getutid_request);
288   request.header.type = UTMPD_REQ_GETUTID;
289   memcpy (&request.id, id, sizeof (struct utmp));
290
291   reply.header.version = UTMPD_VERSION;
292   reply.header.size = sizeof (getutid_reply);
293   reply.header.type = UTMPD_REQ_GETUTID;
294
295   if (send_request (sock, &request.header, &reply.header) < 0)
296     return -1;
297
298   if (reply.result < 0)
299     __set_errno (reply.errnum);
300   else
301     memcpy (buffer, &reply.entry, sizeof (struct utmp));
302
303   return reply.result;
304 }
305
306 static int
307 do_getutline (int sock, const struct utmp *line, struct utmp *buffer)
308 {
309   getutline_request request;
310   getutline_reply reply;
311
312   request.header.version = UTMPD_VERSION;
313   request.header.size = sizeof (getutline_request);
314   request.header.type = UTMPD_REQ_GETUTLINE;
315   memcpy (&request.line, line, sizeof (struct utmp));
316
317   reply.header.version = UTMPD_VERSION;
318   reply.header.size = sizeof (getutline_reply);
319   reply.header.type = UTMPD_REQ_GETUTLINE;
320
321   if (send_request (sock, &request.header, &reply.header) < 0)
322     return -1;
323
324   if (reply.result < 0)
325     __set_errno (reply.errnum);
326   else
327     memcpy (buffer, &reply.entry, sizeof (struct utmp));
328
329   return reply.result;
330 }
331
332 static int
333 do_pututline (int sock, const struct utmp *utmp)
334 {
335   pututline_request request;
336   pututline_reply reply;
337
338   request.header.version = UTMPD_VERSION;
339   request.header.size = sizeof (pututline_request);
340   request.header.type = UTMPD_REQ_PUTUTLINE;
341   memcpy (&request.utmp, utmp, sizeof (struct utmp));
342
343   reply.header.version = UTMPD_VERSION;
344   reply.header.size = sizeof (pututline_reply);
345   reply.header.type = UTMPD_REQ_PUTUTLINE;
346
347   if (send_request (sock, &request.header, &reply.header) < 0)
348     return -1;
349
350   if (reply.result < 0)
351     __set_errno (reply.errnum);
352
353   return reply.result;
354 }
355
356 static int
357 do_endutent (int sock)
358 {
359   endutent_request request;
360   endutent_reply reply;
361
362   request.header.version = UTMPD_VERSION;
363   request.header.size = sizeof (endutent_request);
364   request.header.type = UTMPD_REQ_ENDUTENT;
365
366   reply.header.version = UTMPD_VERSION;
367   reply.header.size = sizeof (endutent_reply);
368   reply.header.type = UTMPD_REQ_ENDUTENT;
369
370   if (send_request (sock, &request.header, &reply.header) < 0)
371     return -1;
372
373   if (reply.result < 0)
374     __set_errno (reply.errnum);
375
376   return reply.result;
377 }
378
379 static int
380 do_updwtmp (int sock, const char *file, const struct utmp *utmp)
381 {
382   updwtmp_request *request;
383   updwtmp_reply reply;
384   size_t size;
385   size_t file_len;
386
387   file_len = strlen (file) + 1;
388   size = sizeof (updwtmp_request) + file_len;
389
390   request = malloc (size);
391   if (request == NULL)
392     return -1;
393
394   request->header.version = UTMPD_VERSION;
395   request->header.size = size;
396   request->header.type = UTMPD_REQ_UPDWTMP;
397   memcpy (&request->utmp, utmp, sizeof (struct utmp));
398   memcpy (request->file, file, file_len);
399
400   reply.header.version = UTMPD_VERSION;
401   reply.header.size = sizeof (updwtmp_reply);
402   reply.header.type = UTMPD_REQ_UPDWTMP;
403
404   if (send_request (sock, &request->header, &reply.header) < 0)
405     {
406       free (request);
407       return -1;
408     }
409
410   if (reply.result < 0)
411     __set_errno (reply.errnum);
412
413   free (request);
414   return reply.result;
415 }
416
417
418 /* Create a socket connected to NAME.  */
419 static int
420 open_socket (const char *name)
421 {
422   struct sockaddr_un addr;
423   int sock;
424
425   sock = __socket (PF_UNIX, SOCK_STREAM, 0);
426   if (sock < 0)
427     return -1;
428
429   addr.sun_family = AF_UNIX;
430   strcpy (addr.sun_path, name);
431   if (__connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
432     {
433       __close (sock);
434       return -1;
435     }
436
437   return sock;
438 }
439
440 /* Send REQUEST to SOCK, and wait for reply.  Returns 0 if successful,
441    storing the reply in REPLY, and -1 if not.  */
442 static int
443 send_request (int sock, const request_header *request,
444               reply_header *reply)
445 {
446   reply_header header;
447   ssize_t nbytes;
448
449   nbytes = __write (sock, request, request->size);
450   if (nbytes != (ssize_t) request->size)
451     return -1;
452
453   nbytes = __read (sock, &header, sizeof (reply_header));
454   if (nbytes != sizeof (reply_header))
455     return -1;
456
457   if (reply->version != header.version
458       || reply->size != header.size
459       || reply->type != header.type)
460     return -1;
461
462   nbytes = __read (sock, reply + 1, reply->size - sizeof (reply_header));
463   if (nbytes != (ssize_t) (reply->size - sizeof (reply_header)))
464     return -1;
465
466   return 0;
467 }