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