]> mj.ucw.cz Git - nwho.git/blob - nwhod.c
Glibc portability checks.
[nwho.git] / nwhod.c
1 /*
2  *      The Remote User Information Daemon 1.8
3  *
4  *      (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU General Public License. See file COPYING in any of the GNU packages.
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdarg.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <sys/socket.h>
17 #include <netdb.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <syslog.h>
21 #include <sys/stat.h>
22 #include <utmp.h>
23 #include <signal.h>
24 #include <dirent.h>
25 #include <asm/types.h>
26 #include <errno.h>
27
28 #include "net.h"
29
30 struct hostrec {
31   struct hostrec *next;
32   char name[32];
33   time_t last_rec;                      /* 0=down */
34   __u32 addr;
35 };
36
37 static int sock, port;
38 static struct hostrec *first_host;
39 static time_t now, last_local_scan;
40
41 static void die(char *msg, ...) __attribute__((noreturn));
42
43 static void
44 die(char *msg, ...)
45 {
46   va_list args;
47
48   va_start(args, msg);
49   fprintf(stderr, "nwhod: ");
50   vfprintf(stderr, msg, args);
51   fputc('\n', stderr);
52   exit(1);
53 }
54
55 static void
56 net_init(char *name)
57 {
58   struct hostent *h;
59   struct sockaddr_in sa;
60   struct servent *se;
61   int one;
62
63   if (name)
64     {
65       if (! (h = gethostbyname(name)))
66         die("%s: %m", name);
67     }
68   else
69     h = NULL;
70
71   if (! (se = getservbyname("nwho", "udp")))
72     die("Unknown service `nwho/udp'");
73
74   sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
75   if (sock < 0)
76     die("socket: %m");
77
78   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
79     die("setsockopt: %m");
80
81   sa.sin_family = AF_INET;
82   sa.sin_port = port = se->s_port;
83   sa.sin_addr.s_addr = INADDR_ANY;
84   if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
85     die("bind: %m");
86
87   if (h)
88     {
89       memcpy(&sa.sin_addr.s_addr, h->h_addr, sizeof(sa.sin_addr.s_addr));
90       if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
91         die("connect: %m");
92     }
93 }
94
95 static void
96 scan_utmp(struct rywho_pkt *p, time_t now)
97 {
98   struct utmp *u;
99   int cnt = 0;
100   struct stat st;
101   char device[32];
102   struct userinfo *h;
103   char name[UT_NAMESIZE+10];
104   time_t last;
105
106   setutent();
107   while ((u = getutent()) && cnt < MAX_USERS)
108     if (u->ut_type == USER_PROCESS && u->ut_user[0])
109       {
110         h = &p->users[cnt];
111         memcpy(name, u->ut_user, UT_NAMESIZE);
112         name[9] = 0;
113         strcpy(h->name, name);
114         h->login_time = htonl(now - u->ut_time);
115         if (u->ut_id[0])
116           {
117             h->con[0] = u->ut_id[0];
118             h->con[1] = u->ut_id[1];
119             h->con[2] = 0;
120           }
121         else
122           {
123             char *z = u->ut_line;
124             if (!strncmp(z, "tty", 3))
125               z += 3;
126             sprintf(h->con, "%.3s", z);
127           }
128         sprintf(device, "/dev/%s", u->ut_line);
129         if (stat(device, &st) < 0)
130           {
131             fprintf(stderr, "stat(%s): %m", device);
132             continue;
133           }
134         h->mesg_y = !!(S_IWGRP & st.st_mode);
135         last = st.st_atime;
136         if (st.st_mtime > last)
137           last = st.st_mtime;
138         if (st.st_ctime > last)
139           last = st.st_ctime;
140         if (now < last)
141           last = now;
142         h->idle_time = htonl(now - last);
143         cnt++;
144       }
145   p->num_users = htonl(cnt);
146 }
147
148 static void
149 scan_load(struct rywho_pkt *p)
150 {
151   int n, i, j[6];
152   char buf[256];
153
154   n = open("/proc/uptime", O_RDONLY);
155   if (n < 0)
156     return;
157   if (read(n, buf, sizeof(buf)) <= 0)
158     return;
159   close(n);
160   if (!sscanf(buf, "%d", &i))
161     return;
162   p->uptime = htonl(i);
163
164   n = open("/proc/loadavg", O_RDONLY);
165   if (n < 0)
166     return;
167   if (read(n, buf, sizeof(buf)) <= 0)
168     return;
169   close(n);
170   if (sscanf(buf, "%d.%d%d.%d%d.%d", &j[0], &j[1], &j[2], &j[3], &j[4], &j[5]) != 6)
171     return;
172   p->avl[0] = htonl(j[0]*100 + j[1]);
173   p->avl[1] = htonl(j[2]*100 + j[3]);
174   p->avl[2] = htonl(j[4]*100 + j[5]);
175 }
176
177 static void
178 make_pkt(struct rywho_pkt *pkt)
179 {
180   bzero(pkt, sizeof(pkt));
181   pkt->local_time = htonl(now);
182   scan_utmp(pkt, now);
183   scan_load(pkt);
184 }
185
186 static void
187 cleanup(void)
188 {
189   DIR *d;
190   struct dirent *e;
191
192   d = opendir(".");
193   if (!d)
194     die("opendir: %m");
195   while (e = readdir(d))
196     if (e->d_name[0] != '.')
197       unlink(e->d_name);
198   closedir(d);
199 }
200
201 static void
202 save_pkt(char *name, struct rywho_pkt *pkt, int len)
203 {
204   int fd = open(name, O_WRONLY | O_CREAT, 0666);
205   if (fd < 0)
206     {
207       syslog(LOG_ERR, "open(%s): %m", name);
208       return;
209     }
210   pkt->server_time = htonl(now);
211   if (write(fd, pkt, len) != len)
212     syslog(LOG_ERR, "write: %m");
213   ftruncate(fd, len);
214   close(fd);
215 }
216
217 static inline int                       /* Validation checks not implemented yet */
218 is_valid(__u32 ipa)
219 {
220   return 1;
221 }
222
223 static void
224 receive(void)
225 {
226   int r, al;
227   struct rywho_pkt pkt;
228   struct sockaddr_in sa;
229   int n = sizeof(struct rywho_pkt) - MAX_USERS * sizeof(struct userinfo);
230   struct hostrec *e;
231   struct hostent *h;
232   char *c;
233
234   alarm(DEFAULT_PRUNE_TIME);
235   al = sizeof(sa);
236   r = recvfrom(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *) &sa, &al);
237   alarm(0);
238   now = time(NULL);
239   if (r < 0)
240     {
241       if (errno == EINTR)
242         return;
243       syslog(LOG_ERR, "recvfrom: %m");
244       sleep(5);
245       return;
246     }
247
248   if (!is_valid(sa.sin_addr.s_addr) || sa.sin_port != port)
249     {
250       syslog(LOG_WARNING, "Received packet from invalid source %s.%d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
251       return;
252     }
253
254   if (r < n || r != n + ntohl(pkt.num_users)*sizeof(struct userinfo))
255     {
256       syslog(LOG_WARNING, "Malformed packet from %s", inet_ntoa(sa.sin_addr));
257       return;
258     }
259
260   for(e=first_host; e; e=e->next)
261     if (e->addr == sa.sin_addr.s_addr)
262       break;
263   if (!e)
264     {
265       e = malloc(sizeof(struct hostrec));
266       if (!e)
267         {
268           syslog(LOG_ERR, "Out of memory");
269           return;
270         }
271       e->next = first_host;
272       first_host = e;
273       h = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
274       if (h)
275         {
276           sprintf(e->name, "%.30s", h->h_name);
277           for(c=e->name; *c; c++)
278             if (*c == '.')
279               {
280                 *c = 0;
281                 break;
282               }
283             else if ((*c < 'A' || *c > 'Z') /* Filter out malicious characters */
284                      && (*c < 'a' || *c > 'z')
285                      && (*c < '0' || *c > '9')
286                      && *c != '-')
287               *c = '_';
288         }
289       else
290         strcpy(e->name, inet_ntoa(sa.sin_addr));
291       e->addr = sa.sin_addr.s_addr;
292     }
293
294   e->last_rec = now;
295   save_pkt(e->name, &pkt, r);
296 }
297
298 static void
299 local_scan(void)
300 {
301   struct rywho_pkt pkt;
302   static char hostname[64];
303
304   make_pkt(&pkt);
305   if (!hostname[0] && gethostname(hostname, sizeof(hostname)) < 0)
306     die("gethostname: %m");
307   save_pkt(hostname, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo));
308 }
309
310 static void
311 do_tick(void)
312 {
313   struct hostrec *e;
314
315   if (last_local_scan + DEFAULT_SEND_TIME <= now)
316     {
317       last_local_scan = now;
318       local_scan();
319     }
320   for(e=first_host; e; e=e->next)
321     if (e->last_rec && e->last_rec + DEFAULT_DEAD_TIME < now)
322       {
323         if (unlink(e->name) < 0)
324           syslog(LOG_ERR, "unlink(%s): %m", e->name);
325         e->last_rec = 0;
326       }
327 }
328
329 static void
330 tick(int unused)
331 {
332 }
333
334 static void
335 server(void)
336 {
337   static struct sigaction sigact;
338
339   net_init(NULL);
340   if (chdir(YWHO_SPOOL_DIR) < 0)
341     die("chdir(" YWHO_SPOOL_DIR "): %m");
342   cleanup();
343   bzero(&sigact, sizeof(sigact));
344   sigact.sa_handler = tick;
345   sigaction(SIGALRM, &sigact, NULL);
346   now = time(NULL);
347   for(;;)
348     {
349       do_tick();
350       receive();
351     }
352 }
353
354 static void
355 client(char *serv)
356 {
357   struct rywho_pkt pkt;
358
359   net_init(serv);
360   utmpname(UTMP_FILE);
361   for(;;)
362     {
363       now = time(NULL);
364       make_pkt(&pkt);
365       if (send(sock, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo), 0) < 0)
366         syslog(LOG_ERR, "sendmsg: %m");
367       sleep(DEFAULT_SEND_TIME);
368     }
369 }
370
371 int
372 main(int argc, char **argv)
373 {
374   int pid;
375
376   if (argc != 1 && argc != 2)
377     {
378       fprintf(stderr, "Usage: nwhod [<server>]\n");
379       return 1;
380     }
381   pid = fork();
382   if (pid < 0)
383     {
384       perror("fork");
385       return 1;
386     }
387   if (pid)
388     return 0;
389   if (argc == 2)
390     client(argv[1]);
391   else if (argc == 1)
392     server();
393   return 1;
394 }