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