1 /*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/ioctl.h>
29
30 #include <curses.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include "dhcpcd-curses.h"
40
41 #ifdef HAVE_NC_FREE_AND_EXIT
42 void _nc_free_and_exit(void);
43 #endif
44
45 static const int sigs[] = {
46 SIGHUP,
47 SIGINT,
48 SIGPIPE,
49 SIGTERM,
50 SIGWINCH
51 };
52
53 #ifndef __arraycount
54 #define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
55 #endif
56
57 static void try_open(void *);
58
59 static void
set_status(struct ctx * ctx,const char * status)60 set_status(struct ctx *ctx, const char *status)
61 {
62 int w;
63 size_t slen;
64
65 w = getmaxx(ctx->win_status);
66 w -= (int)(slen = strlen(status));
67 if (ctx->status_len > slen) {
68 wmove(ctx->win_status, 0, w - (int)(ctx->status_len - slen));
69 wclrtoeol(ctx->win_status);
70 }
71 mvwprintw(ctx->win_status, 0, w, "%s", status);
72 wrefresh(ctx->win_status);
73 ctx->status_len = slen;
74 }
75
76 static int
set_summary(struct ctx * ctx,const char * msg)77 set_summary(struct ctx *ctx, const char *msg)
78 {
79 int r;
80
81 wclear(ctx->win_summary);
82 if (msg)
83 r = wprintw(ctx->win_summary, "%s", msg);
84 else
85 r = 0;
86 wrefresh(ctx->win_summary);
87 return r;
88 }
89
90 __printflike(2, 3) static int
debug(struct ctx * ctx,const char * fmt,...)91 debug(struct ctx *ctx, const char *fmt, ...)
92 {
93 va_list args;
94 int r;
95
96 if (ctx->win_debug == NULL)
97 return 0;
98 waddch(ctx->win_debug, '\n');
99 va_start(args, fmt);
100 r = vw_printw(ctx->win_debug, fmt, args);
101 va_end(args);
102 wrefresh(ctx->win_debug);
103 return r;
104 }
105
106 __printflike(2, 3) static int
warning(struct ctx * ctx,const char * fmt,...)107 warning(struct ctx *ctx, const char *fmt, ...)
108 {
109 va_list args;
110 int r;
111
112 if (ctx->win_debug == NULL)
113 return 0;
114 waddch(ctx->win_debug, '\n');
115 va_start(args, fmt);
116 r = vw_printw(ctx->win_debug, fmt, args);
117 va_end(args);
118 wrefresh(ctx->win_debug);
119 return r;
120 }
121
122 __printflike(2, 3) static int
notify(struct ctx * ctx,const char * fmt,...)123 notify(struct ctx *ctx, const char *fmt, ...)
124 {
125 va_list args;
126 int r;
127
128 if (ctx->win_debug == NULL)
129 return 0;
130 waddch(ctx->win_debug, '\n');
131 va_start(args, fmt);
132 r = vw_printw(ctx->win_debug, fmt, args);
133 va_end(args);
134 wrefresh(ctx->win_debug);
135 return r;
136 }
137
138 static void
update_online(struct ctx * ctx,bool show_if)139 update_online(struct ctx *ctx, bool show_if)
140 {
141 bool online, carrier;
142 char *msg, *msgs, *nmsg;
143 size_t msgs_len, mlen;
144 DHCPCD_IF *ifs, *i;
145
146 online = carrier = false;
147 msgs = NULL;
148 msgs_len = 0;
149 ifs = dhcpcd_interfaces(ctx->con);
150 for (i = ifs; i; i = i->next) {
151 if (i->type == DHT_LINK) {
152 if (i->up)
153 carrier = true;
154 } else {
155 if (i->up)
156 online = true;
157 }
158 msg = dhcpcd_if_message(i, NULL);
159 if (msg) {
160 if (show_if) {
161 if (i->up)
162 notify(ctx, "%s", msg);
163 else
164 warning(ctx, "%s", msg);
165 }
166 if (msgs == NULL) {
167 msgs = msg;
168 msgs_len = strlen(msgs) + 1;
169 } else {
170 mlen = strlen(msg) + 1;
171 nmsg = realloc(msgs, msgs_len + mlen);
172 if (nmsg) {
173 msgs = nmsg;
174 msgs[msgs_len - 1] = '\n';
175 memcpy(msgs + msgs_len, msg, mlen);
176 msgs_len += mlen;
177 } else
178 warn("realloc");
179 free(msg);
180 }
181 } else if (show_if) {
182 if (i->up)
183 notify(ctx, "%s: %s", i->ifname, i->reason);
184 else
185 warning(ctx, "%s: %s", i->ifname, i->reason);
186 }
187 }
188
189 set_summary(ctx, msgs);
190 free(msgs);
191 }
192
193 static void
dispatch(void * arg)194 dispatch(void *arg)
195 {
196 struct ctx *ctx = arg;
197
198 dhcpcd_dispatch(ctx->con);
199 }
200
201 static void
try_open(void * arg)202 try_open(void *arg)
203 {
204 struct ctx *ctx = arg;
205 static int last_error;
206 int fd;
207
208 fd = dhcpcd_open(ctx->con, true);
209 if (fd == -1) {
210 if (errno == EACCES || errno == EPERM) {
211 fd = dhcpcd_open(ctx->con, false);
212 if (fd != -1)
213 goto unprived;
214 }
215 if (errno != last_error) {
216 last_error = errno;
217 set_status(ctx, strerror(errno));
218 }
219 eloop_timeout_add_msec(ctx->eloop, DHCPCD_RETRYOPEN,
220 try_open, ctx);
221 return;
222 }
223
224 unprived:
225 last_error = 0;
226
227 /* Start listening to WPA events */
228 dhcpcd_wpa_start(ctx->con);
229
230 eloop_event_add(ctx->eloop, fd, dispatch, ctx, NULL, NULL);
231 }
232
233 static void
status_cb(DHCPCD_CONNECTION * con,unsigned int status,const char * status_msg,void * arg)234 status_cb(DHCPCD_CONNECTION *con,
235 unsigned int status, const char *status_msg, void *arg)
236 {
237 struct ctx *ctx = arg;
238
239 debug(ctx, _("Status changed to %s"), status_msg);
240 set_status(ctx, status_msg);
241
242 if (status == DHC_DOWN) {
243 int fd;
244
245 fd = dhcpcd_get_fd(ctx->con);
246 eloop_event_delete(ctx->eloop, fd);
247 ctx->online = ctx->carrier = false;
248 eloop_timeout_delete(ctx->eloop, NULL, ctx);
249 set_summary(ctx, NULL);
250 eloop_timeout_add_msec(ctx->eloop, DHCPCD_RETRYOPEN,
251 try_open, ctx);
252 } else {
253 bool refresh;
254
255 if (ctx->last_status == DHC_UNKNOWN ||
256 ctx->last_status == DHC_DOWN)
257 {
258 debug(ctx, _("Connected to dhcpcd-%s"),
259 dhcpcd_version(con));
260 refresh = true;
261 } else
262 refresh =
263 ctx->last_status == DHC_OPENED ? true : false;
264 update_online(ctx, refresh);
265 }
266
267 ctx->last_status = status;
268 }
269
270 static void
if_cb(DHCPCD_IF * i,void * arg)271 if_cb(DHCPCD_IF *i, void *arg)
272 {
273 struct ctx *ctx = arg;
274
275 if (i->state == DHS_RENEW ||
276 i->state == DHS_STOP || i->state == DHS_STOPPED)
277 {
278 char *msg;
279 bool new_msg;
280
281 msg = dhcpcd_if_message(i, &new_msg);
282 if (msg) {
283 if (i->up)
284 warning(ctx, "%s", msg);
285 else
286 notify(ctx, "%s", msg);
287 free(msg);
288 }
289 }
290
291 update_online(ctx, false);
292
293 if (i->wireless) {
294 /* PROCESS SCANS */
295 }
296 }
297
298 static void
wpa_dispatch(void * arg)299 wpa_dispatch(void *arg)
300 {
301 DHCPCD_WPA *wpa = arg;
302
303 dhcpcd_wpa_dispatch(wpa);
304 }
305
306 static void
wpa_scan_cb(DHCPCD_WPA * wpa,void * arg)307 wpa_scan_cb(DHCPCD_WPA *wpa, void *arg)
308 {
309 struct ctx *ctx = arg;
310 DHCPCD_IF *i;
311 WI_SCAN *wi;
312 DHCPCD_WI_SCAN *scans, *s1, *s2;
313 int fd, lerrno;
314
315 /* This could be a new WPA so watch it */
316 if ((fd = dhcpcd_wpa_get_fd(wpa)) == -1) {
317 debug(ctx, "%s (%p)", _("no fd for WPA"), wpa);
318 return;
319 }
320 eloop_event_add(ctx->eloop, fd, wpa_dispatch, wpa, NULL, NULL);
321
322 i = dhcpcd_wpa_if(wpa);
323 if (i == NULL) {
324 debug(ctx, "%s (%p)", _("No interface for WPA"), wpa);
325 return;
326 }
327 debug(ctx, "%s: %s", i->ifname, _("Received scan results"));
328 lerrno = errno;
329 errno = 0;
330 scans = dhcpcd_wi_scans(i);
331 if (scans == NULL && errno)
332 debug(ctx, "%s: %s", i->ifname, strerror(errno));
333 errno = lerrno;
334 TAILQ_FOREACH(wi, &ctx->wi_scans, next) {
335 if (wi->interface == i)
336 break;
337 }
338 if (wi == NULL) {
339 wi = malloc(sizeof(*wi));
340 wi->interface = i;
341 wi->scans = scans;
342 TAILQ_INSERT_TAIL(&ctx->wi_scans, wi, next);
343 } else {
344 const char *title;
345 char *msgs, *nmsg;
346 size_t msgs_len, mlen;
347
348 title = NULL;
349 msgs = NULL;
350 for (s1 = scans; s1; s1 = s1->next) {
351 for (s2 = wi->scans; s2; s2 = s2->next)
352 if (strcmp(s1->ssid, s2->ssid) == 0)
353 break;
354 if (s2 == NULL) {
355 if (msgs == NULL) {
356 msgs = strdup(s1->ssid);
357 msgs_len = strlen(msgs) + 1;
358 } else {
359 if (title == NULL)
360 title = _("New Access Points");
361 mlen = strlen(s1->ssid) + 1;
362 nmsg = realloc(msgs, msgs_len + mlen);
363 if (nmsg) {
364 msgs = nmsg;
365 msgs[msgs_len - 1] = '\n';
366 memcpy(msgs + msgs_len,
367 s1->ssid, mlen);
368 msgs_len += mlen;
369 } else
370 warn("realloc");
371 }
372 }
373 }
374 if (msgs) {
375 if (title == NULL)
376 title = _("New Access Point");
377 mlen = strlen(title) + 1;
378 nmsg = realloc(msgs, msgs_len + mlen);
379 if (nmsg) {
380 msgs = nmsg;
381 memmove(msgs + mlen, msgs, msgs_len);
382 memcpy(msgs, title, mlen);
383 msgs[mlen - 1] = '\n';
384 } else
385 warn("realloc");
386 notify(ctx, "%s", msgs);
387 free(msgs);
388 }
389
390 dhcpcd_wi_scans_free(wi->scans);
391 wi->scans = scans;
392 }
393 }
394
395 static void
wpa_status_cb(DHCPCD_WPA * wpa,unsigned int status,const char * status_msg,void * arg)396 wpa_status_cb(DHCPCD_WPA *wpa,
397 unsigned int status, const char *status_msg, void *arg)
398 {
399 struct ctx *ctx = arg;
400 DHCPCD_IF *i;
401 WI_SCAN *w, *wn;
402
403 i = dhcpcd_wpa_if(wpa);
404 debug(ctx, _("%s: WPA status %s"), i->ifname, status_msg);
405 if (status == DHC_DOWN) {
406 int fd;
407
408 fd = dhcpcd_wpa_get_fd(wpa);
409 eloop_event_delete(ctx->eloop, fd);
410 dhcpcd_wpa_close(wpa);
411 TAILQ_FOREACH_SAFE(w, &ctx->wi_scans, next, wn) {
412 if (w->interface == i) {
413 TAILQ_REMOVE(&ctx->wi_scans, w, next);
414 dhcpcd_wi_scans_free(w->scans);
415 free(w);
416 }
417 }
418 }
419 }
420
421 static void
bg_scan(void * arg)422 bg_scan(void *arg)
423 {
424 struct ctx *ctx = arg;
425 WI_SCAN *w;
426 DHCPCD_WPA *wpa;
427
428 TAILQ_FOREACH(w, &ctx->wi_scans, next) {
429 if (w->interface->wireless& w->interface->up) {
430 wpa = dhcpcd_wpa_find(ctx->con, w->interface->ifname);
431 if (wpa &&
432 (!w->interface->up ||
433 dhcpcd_wpa_can_background_scan(wpa)))
434 dhcpcd_wpa_scan(wpa);
435 }
436 }
437
438 eloop_timeout_add_msec(ctx->eloop, DHCPCD_WPA_SCAN_SHORT,
439 bg_scan, ctx);
440 }
441
442 static void
signal_cb(int sig,void * arg)443 signal_cb(int sig, void *arg)
444 {
445 struct ctx *ctx = arg;
446 struct winsize ws;
447
448 switch(sig) {
449 case SIGWINCH:
450 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0)
451 resizeterm(ws.ws_row, ws.ws_col);
452 break;
453 case SIGINT:
454 debug(ctx, _("SIGINT caught, exiting"));
455 eloop_exit(ctx->eloop, EXIT_FAILURE);
456 break;
457 case SIGTERM:
458 debug(ctx, _("SIGTERM caught, exiting"));
459 eloop_exit(ctx->eloop, EXIT_FAILURE);
460 break;
461 case SIGHUP:
462 debug(ctx, ("SIGHUP caught, ignoring"));
463 break;
464 case SIGPIPE:
465 /* ignore and don't report */
466 break;
467 }
468 }
469
470 static int
create_windows(struct ctx * ctx)471 create_windows(struct ctx *ctx)
472 {
473 int h, w;
474
475 getmaxyx(ctx->stdscr, h, w);
476
477 if ((ctx->win_status = newwin(1, w, 0, 0)) == NULL)
478 return -1;
479
480 if ((ctx->win_summary_border = newwin(10, w - 2, 2, 1)) == NULL)
481 return -1;
482 box(ctx->win_summary_border, 0, 0);
483 mvwprintw(ctx->win_summary_border, 0, 5, " %s ",
484 _("Connection Summary"));
485 wrefresh(ctx->win_summary_border);
486 if ((ctx->win_summary = newwin(8, w - 4, 3, 2)) == NULL)
487 return -1;
488 scrollok(ctx->win_summary, TRUE);
489
490 #if 1
491 if ((ctx->win_debug_border = newwin(8, w - 2, h - 10, 1)) == NULL)
492 return -1;
493 box(ctx->win_debug_border, 0, 0);
494 mvwprintw(ctx->win_debug_border, 0, 5, " %s ",
495 _("Event Log"));
496 wrefresh(ctx->win_debug_border);
497 if ((ctx->win_debug = newwin(6, w - 4, h - 9, 2)) == NULL)
498 return -1;
499 scrollok(ctx->win_debug, TRUE);
500 #endif
501 return 0;
502 }
503
504 int
main(void)505 main(void)
506 {
507 struct ctx ctx;
508 WI_SCAN *wi;
509 sigset_t sigmask;
510
511 memset(&ctx, 0, sizeof(ctx));
512 TAILQ_INIT(&ctx.wi_scans);
513
514 if ((ctx.eloop = eloop_new()) == NULL)
515 err(EXIT_FAILURE, "eloop_new");
516 if (eloop_signal_set_cb(ctx.eloop, sigs, __arraycount(sigs),
517 signal_cb, &ctx) == -1)
518 err(EXIT_FAILURE, "eloop_signal_set_cb");
519 if (eloop_signal_mask(ctx.eloop, &sigmask) == -1)
520 err(EXIT_FAILURE, "eloop_signal_mask");
521
522 if ((ctx.con = dhcpcd_new()) == NULL)
523 err(EXIT_FAILURE, "dhcpcd_new");
524
525 if ((ctx.stdscr = initscr()) == NULL)
526 err(EXIT_FAILURE, "initscr");
527
528 if (create_windows(&ctx) == -1)
529 err(EXIT_FAILURE, "create_windows");
530
531 curs_set(0);
532 noecho();
533 keypad(ctx.stdscr, TRUE);
534
535 wprintw(ctx.win_status, "%s %s", _("dhcpcd Curses Interface"), VERSION);
536 dhcpcd_set_progname(ctx.con, "dhcpcd-curses");
537 dhcpcd_set_status_callback(ctx.con, status_cb, &ctx);
538 dhcpcd_set_if_callback(ctx.con, if_cb, &ctx);
539 dhcpcd_wpa_set_scan_callback(ctx.con, wpa_scan_cb, &ctx);
540 dhcpcd_wpa_set_status_callback(ctx.con, wpa_status_cb, &ctx);
541
542 eloop_timeout_add_sec(ctx.eloop, 0, try_open, &ctx);
543 eloop_timeout_add_msec(ctx.eloop, DHCPCD_WPA_SCAN_SHORT,
544 bg_scan, &ctx);
545 eloop_start(ctx.eloop, &sigmask);
546
547 /* Un-resgister the callbacks to avoid spam on close */
548 dhcpcd_set_status_callback(ctx.con, NULL, NULL);
549 dhcpcd_set_if_callback(ctx.con, NULL, NULL);
550 dhcpcd_wpa_set_scan_callback(ctx.con, NULL, NULL);
551 dhcpcd_wpa_set_status_callback(ctx.con, NULL, NULL);
552 dhcpcd_close(ctx.con);
553 dhcpcd_free(ctx.con);
554
555 /* Free our saved scans */
556 while ((wi = TAILQ_FIRST(&ctx.wi_scans))) {
557 TAILQ_REMOVE(&ctx.wi_scans, wi, next);
558 dhcpcd_wi_scans_free(wi->scans);
559 free(wi);
560 }
561
562 /* Free everything else */
563 eloop_free(ctx.eloop);
564 endwin();
565
566 #ifdef HAVE_NC_FREE_AND_EXIT
567 /* undefined ncurses function to allow valgrind debugging */
568 _nc_free_and_exit();
569 #endif
570
571 return EXIT_SUCCESS;
572 }
573