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