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