]> mj.ucw.cz Git - nwho.git/blob - nwhod.c
Let save_pkt() calculates the packet length internally
[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)
174 {
175   int len = sizeof(pkt) - (MAX_USERS - ntohl(pkt->num_users))*sizeof(struct userinfo);
176   int fd = open(name, O_WRONLY | O_CREAT, 0666);
177   if (fd < 0)
178     {
179       syslog(LOG_ERR, "open(%s): %m", name);
180       return;
181     }
182   pkt->server_time = htonl(now);
183   if (write(fd, pkt, len) != len)
184     syslog(LOG_ERR, "write: %m");
185   ftruncate(fd, len);
186   close(fd);
187 }
188
189 static inline int                       /* Validation checks not implemented yet */
190 is_valid(u32 ipa)
191 {
192   return 1;
193 }
194
195 static void
196 receive(void)
197 {
198   int r;
199   struct nwho_pkt pkt;
200   struct sockaddr_in sa;
201   socklen_t al = sizeof(sa);
202   int n = sizeof(struct nwho_pkt) - MAX_USERS * sizeof(struct userinfo);
203   struct hostrec *e;
204   struct hostent *h;
205   char *c;
206
207   alarm(DEFAULT_PRUNE_TIME);
208   r = recvfrom(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *) &sa, &al);
209   alarm(0);
210   now = time(NULL);
211   if (r < 0)
212     {
213       if (errno == EINTR)
214         return;
215       syslog(LOG_ERR, "recvfrom: %m");
216       sleep(5);
217       return;
218     }
219
220   if (!is_valid(sa.sin_addr.s_addr) || sa.sin_port != port)
221     {
222       syslog(LOG_WARNING, "Received packet from invalid source %s:%d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
223       return;
224     }
225
226   if (r < n ||
227       pkt.magic != htonl(NWHO_MAGIC) ||
228       ntohl(pkt.num_users) > MAX_USERS ||
229       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   for(e=first_host; e; e=e->next)
236     if (e->addr == sa.sin_addr.s_addr)
237       break;
238
239   if (!e)
240     {
241       e = malloc(sizeof(struct hostrec));
242       if (!e)
243         {
244           syslog(LOG_ERR, "Out of memory");
245           return;
246         }
247       e->next = first_host;
248       first_host = e;
249       h = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
250       if (h)
251         {
252           snprintf(e->name, sizeof(e->name), "%s", h->h_name);
253           for (c=e->name; *c; c++)
254             if (*c == '.')
255               {
256                 *c = 0;
257                 break;
258               }
259             else if ((*c < 'A' || *c > 'Z') /* Filter out malicious characters */
260                      && (*c < 'a' || *c > 'z')
261                      && (*c < '0' || *c > '9')
262                      && *c != '-')
263               *c = '_';
264         }
265       else
266         strcpy(e->name, inet_ntoa(sa.sin_addr));
267       e->addr = sa.sin_addr.s_addr;
268     }
269
270   e->last_rec = now;
271   save_pkt(e->name, &pkt);
272 }
273
274 static void
275 local_scan(void)
276 {
277   struct nwho_pkt pkt;
278   static char hostname[64];
279
280   make_pkt(&pkt);
281   if (!hostname[0] && gethostname(hostname, sizeof(hostname)) < 0)
282     die("gethostname: %m");
283   save_pkt(hostname, &pkt);
284 }
285
286 static void
287 do_tick(void)
288 {
289   struct hostrec *e;
290
291   if (last_local_scan + DEFAULT_SEND_TIME <= now)
292     {
293       last_local_scan = now;
294       local_scan();
295     }
296   for(e=first_host; e; e=e->next)
297     if (e->last_rec && e->last_rec + DEFAULT_DEAD_TIME < now)
298       {
299         if (unlink(e->name) < 0)
300           syslog(LOG_ERR, "unlink(%s): %m", e->name);
301         e->last_rec = 0;
302       }
303 }
304
305 static void
306 daemonize(void)
307 {
308   pid_t pid = fork();
309   if (pid < 0)
310     die("Fork failed: %m");
311   if (pid)
312     exit(0);
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 nwho_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   if (argc == 2)
371     client(argv[1]);
372   else if (argc == 1)
373     server();
374   else
375     {
376       fprintf(stderr, "Usage: nwhod [<server>]\n");
377       return 1;
378     }
379   return 0;
380 }