]> mj.ucw.cz Git - nwho.git/blob - nwhod.c
5f3b0aca32ae4baae3adce0e2c53b5695ba1011d
[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("%s: %m", name);
62     }
63   else
64     h = NULL;
65
66   sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
67   if (sock < 0)
68     die("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("bind: %m");
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("connect: %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         sprintf(h->con, "%.7s", u->ut_line);
105         sprintf(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 (st.st_mtime > last)
111           last = st.st_mtime;
112         if (st.st_ctime > last)
113           last = st.st_ctime;
114         if (now < last)
115           last = now;
116         h->idle_time = htonl(now - last);
117         cnt++;
118       }
119   p->num_users = htonl(cnt);
120 }
121
122 static void
123 scan_load(struct nwho_pkt *p)
124 {
125   int n, i, j[6];
126   char buf[256];
127
128   n = open("/proc/uptime", O_RDONLY);
129   if (n < 0)
130     return;
131   if (read(n, buf, sizeof(buf)) <= 0)
132     return;
133   close(n);
134   if (!sscanf(buf, "%d", &i))
135     return;
136   p->uptime = htonl(i);
137
138   n = open("/proc/loadavg", O_RDONLY);
139   if (n < 0)
140     return;
141   if (read(n, buf, sizeof(buf)) <= 0)
142     return;
143   close(n);
144   if (sscanf(buf, "%d.%d%d.%d%d.%d", &j[0], &j[1], &j[2], &j[3], &j[4], &j[5]) != 6)
145     return;
146   p->avl[0] = htonl(j[0]*100 + j[1]);
147   p->avl[1] = htonl(j[2]*100 + j[3]);
148   p->avl[2] = htonl(j[4]*100 + j[5]);
149 }
150
151 static void
152 make_pkt(struct nwho_pkt *pkt)
153 {
154   bzero(pkt, sizeof(pkt));
155   pkt->magic = htonl(NWHO_MAGIC);
156   pkt->local_time = htonl(now);
157   scan_utmp(pkt, now);
158   scan_load(pkt);
159 }
160
161 static void
162 cleanup(void)
163 {
164   DIR *d;
165   struct dirent *e;
166
167   d = opendir(".");
168   if (!d)
169     die("opendir: %m");
170   while (e = readdir(d))
171     if (e->d_name[0] != '.')
172       unlink(e->d_name);
173   closedir(d);
174 }
175
176 static void
177 save_pkt(char *name, struct nwho_pkt *pkt, int len)
178 {
179   int fd = open(name, O_WRONLY | O_CREAT, 0666);
180   if (fd < 0)
181     {
182       syslog(LOG_ERR, "open(%s): %m", name);
183       return;
184     }
185   pkt->server_time = htonl(now);
186   if (write(fd, pkt, len) != len)
187     syslog(LOG_ERR, "write: %m");
188   ftruncate(fd, len);
189   close(fd);
190 }
191
192 static inline int                       /* Validation checks not implemented yet */
193 is_valid(u32 ipa)
194 {
195   return 1;
196 }
197
198 static void
199 receive(void)
200 {
201   int r;
202   struct nwho_pkt pkt;
203   struct sockaddr_in sa;
204   socklen_t al = sizeof(sa);
205   int n = sizeof(struct nwho_pkt) - MAX_USERS * sizeof(struct userinfo);
206   struct hostrec *e;
207   struct hostent *h;
208   char *c;
209
210   alarm(DEFAULT_PRUNE_TIME);
211   r = recvfrom(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *) &sa, &al);
212   alarm(0);
213   now = time(NULL);
214   if (r < 0)
215     {
216       if (errno == EINTR)
217         return;
218       syslog(LOG_ERR, "recvfrom: %m");
219       sleep(5);
220       return;
221     }
222
223   if (!is_valid(sa.sin_addr.s_addr) || sa.sin_port != port)
224     {
225       syslog(LOG_WARNING, "Received packet from invalid source %s.%d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
226       return;
227     }
228
229   if (r < n || r != n + ntohl(pkt.num_users)*sizeof(struct userinfo))
230     {
231       syslog(LOG_WARNING, "Malformed packet from %s", inet_ntoa(sa.sin_addr));
232       return;
233     }
234
235   if (pkt.magic != htonl(NWHO_MAGIC))
236     {
237       syslog(LOG_WARNING, "Received ancient nwho packet from %s", inet_ntoa(sa.sin_addr));
238       return;
239     }
240
241   for(e=first_host; e; e=e->next)
242     if (e->addr == sa.sin_addr.s_addr)
243       break;
244   if (!e)
245     {
246       e = malloc(sizeof(struct hostrec));
247       if (!e)
248         {
249           syslog(LOG_ERR, "Out of memory");
250           return;
251         }
252       e->next = first_host;
253       first_host = e;
254       h = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
255       if (h)
256         {
257           sprintf(e->name, "%.30s", h->h_name);
258           for(c=e->name; *c; c++)
259             if (*c == '.')
260               {
261                 *c = 0;
262                 break;
263               }
264             else if ((*c < 'A' || *c > 'Z') /* Filter out malicious characters */
265                      && (*c < 'a' || *c > 'z')
266                      && (*c < '0' || *c > '9')
267                      && *c != '-')
268               *c = '_';
269         }
270       else
271         strcpy(e->name, inet_ntoa(sa.sin_addr));
272       e->addr = sa.sin_addr.s_addr;
273     }
274
275   e->last_rec = now;
276   save_pkt(e->name, &pkt, r);
277 }
278
279 static void
280 local_scan(void)
281 {
282   struct nwho_pkt pkt;
283   static char hostname[64];
284
285   make_pkt(&pkt);
286   if (!hostname[0] && gethostname(hostname, sizeof(hostname)) < 0)
287     die("gethostname: %m");
288   save_pkt(hostname, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo));
289 }
290
291 static void
292 do_tick(void)
293 {
294   struct hostrec *e;
295
296   if (last_local_scan + DEFAULT_SEND_TIME <= now)
297     {
298       last_local_scan = now;
299       local_scan();
300     }
301   for(e=first_host; e; e=e->next)
302     if (e->last_rec && e->last_rec + DEFAULT_DEAD_TIME < now)
303       {
304         if (unlink(e->name) < 0)
305           syslog(LOG_ERR, "unlink(%s): %m", e->name);
306         e->last_rec = 0;
307       }
308 }
309
310 static void
311 daemonize(void)
312 {
313   pid_t pid = fork();
314   if (pid < 0)
315     die("Fork failed: %m");
316   if (pid)
317     exit(0);
318
319   close(0);
320   close(1);
321   close(2);
322   open("/dev/null", O_RDONLY);
323   dup(0);
324   dup(0);
325   setsid();
326 }
327
328 static void
329 tick(int unused)
330 {
331 }
332
333 static void
334 server(void)
335 {
336   static struct sigaction sigact;
337
338   net_init(NULL);
339   if (chdir(NWHO_SPOOL_DIR) < 0)
340     die("chdir(" NWHO_SPOOL_DIR "): %m");
341   cleanup();
342   daemonize();
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 nwho_pkt pkt;
358
359   net_init(serv);
360   utmpname(UTMP_FILE);
361   daemonize();
362   for(;;)
363     {
364       now = time(NULL);
365       make_pkt(&pkt);
366       if (send(sock, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo), 0) < 0)
367         syslog(LOG_ERR, "sendmsg: %m");
368       sleep(DEFAULT_SEND_TIME);
369     }
370 }
371
372 int
373 main(int argc, char **argv)
374 {
375   if (argc == 2)
376     client(argv[1]);
377   else if (argc == 1)
378     server();
379   else
380     {
381       fprintf(stderr, "Usage: nwhod [<server>]\n");
382       return 1;
383     }
384   return 0;
385 }