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