]> mj.ucw.cz Git - nwho.git/blob - nwhod.c
872a68d5e72b8d5cfb10623a2cd02f13b2e5e5da
[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 static char hostname[64];
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("Failed to resolve %s", name);
63     }
64   else
65     h = NULL;
66
67   sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
68   if (sock < 0)
69     die("Cannot create 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("Cannot bind to UDP port %d: %m", NWHO_PORT);
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("Cannot connect socket: %m");
82     }
83
84   if (gethostname(hostname, sizeof(hostname)) < 0)
85     die("Unable to get my own host name: %m");
86 }
87
88 static void
89 scan_utmp(struct nwho_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         snprintf(h->con, sizeof(h->con), "%s", u->ut_line);
109         snprintf(device, sizeof(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 (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)
178 {
179   int len = sizeof(pkt) - (MAX_USERS - ntohl(pkt->num_users))*sizeof(struct userinfo);
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;
203   struct nwho_pkt pkt;
204   struct sockaddr_in sa;
205   socklen_t al = sizeof(sa);
206   int n = sizeof(struct nwho_pkt) - MAX_USERS * sizeof(struct userinfo);
207   struct hostrec *e;
208   struct hostent *h;
209   char *c;
210
211   alarm(DEFAULT_PRUNE_TIME);
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 ||
231       pkt.magic != htonl(NWHO_MAGIC) ||
232       ntohl(pkt.num_users) > MAX_USERS ||
233       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   for(e=first_host; e; e=e->next)
240     if (e->addr == sa.sin_addr.s_addr)
241       break;
242
243   if (!e)
244     {
245       e = malloc(sizeof(struct hostrec));
246       if (!e)
247         {
248           syslog(LOG_ERR, "Out of memory");
249           return;
250         }
251       e->next = first_host;
252       first_host = e;
253       h = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
254       if (h)
255         {
256           snprintf(e->name, sizeof(e->name), "%s", h->h_name);
257           for (c=e->name; *c; c++)
258             if (*c == '.')
259               {
260                 *c = 0;
261                 break;
262               }
263             else if ((*c < 'A' || *c > 'Z') /* Filter out malicious characters */
264                      && (*c < 'a' || *c > 'z')
265                      && (*c < '0' || *c > '9')
266                      && *c != '-')
267               *c = '_';
268         }
269       else
270         strcpy(e->name, inet_ntoa(sa.sin_addr));
271       e->addr = sa.sin_addr.s_addr;
272     }
273
274   e->last_rec = now;
275   save_pkt(e->name, &pkt);
276 }
277
278 static void
279 local_scan(void)
280 {
281   struct nwho_pkt pkt;
282
283   make_pkt(&pkt);
284   save_pkt(hostname, &pkt);
285 }
286
287 static void
288 do_tick(void)
289 {
290   struct hostrec *e;
291
292   if (last_local_scan + DEFAULT_SEND_TIME <= now)
293     {
294       last_local_scan = now;
295       local_scan();
296     }
297   for(e=first_host; e; e=e->next)
298     if (e->last_rec && e->last_rec + DEFAULT_DEAD_TIME < now)
299       {
300         if (unlink(e->name) < 0)
301           syslog(LOG_ERR, "unlink(%s): %m", e->name);
302         e->last_rec = 0;
303       }
304 }
305
306 static void
307 daemonize(void)
308 {
309   pid_t pid = fork();
310   if (pid < 0)
311     die("Fork failed: %m");
312   if (pid)
313     exit(0);
314
315   close(0);
316   close(1);
317   close(2);
318   open("/dev/null", O_RDONLY);
319   dup(0);
320   dup(0);
321   setsid();
322 }
323
324 static void
325 tick(int unused)
326 {
327 }
328
329 static void
330 server(void)
331 {
332   static struct sigaction sigact;
333
334   net_init(NULL);
335   if (chdir(NWHO_SPOOL_DIR) < 0)
336     die("chdir(" NWHO_SPOOL_DIR "): %m");
337   cleanup();
338   daemonize();
339   bzero(&sigact, sizeof(sigact));
340   sigact.sa_handler = tick;
341   sigaction(SIGALRM, &sigact, NULL);
342   now = time(NULL);
343   for(;;)
344     {
345       do_tick();
346       receive();
347     }
348 }
349
350 static void
351 client(char *serv)
352 {
353   struct nwho_pkt pkt;
354
355   net_init(serv);
356   utmpname(UTMP_FILE);
357   daemonize();
358   for(;;)
359     {
360       now = time(NULL);
361       make_pkt(&pkt);
362       if (send(sock, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo), 0) < 0)
363         syslog(LOG_ERR, "sendmsg: %m");
364       sleep(DEFAULT_SEND_TIME);
365     }
366 }
367
368 int
369 main(int argc, char **argv)
370 {
371   if (argc == 2)
372     client(argv[1]);
373   else if (argc == 1)
374     server();
375   else
376     {
377       fprintf(stderr, "Usage: nwhod [<server>]\n");
378       return 1;
379     }
380   return 0;
381 }