1 |
|
|
/* $OpenBSD: npppctl.c,v 1.8 2017/08/11 16:25:59 goda Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2012 Internet Initiative Japan Inc. |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
#include <sys/queue.h> |
19 |
|
|
#include <sys/socket.h> |
20 |
|
|
#include <sys/un.h> |
21 |
|
|
#include <sys/uio.h> |
22 |
|
|
#include <net/if.h> |
23 |
|
|
#include <net/if_dl.h> |
24 |
|
|
#include <netinet/in.h> |
25 |
|
|
#include <arpa/inet.h> |
26 |
|
|
|
27 |
|
|
#include <errno.h> |
28 |
|
|
#include <netdb.h> |
29 |
|
|
#include <stdbool.h> |
30 |
|
|
#include <stddef.h> |
31 |
|
|
#include <stdio.h> |
32 |
|
|
#include <stdlib.h> |
33 |
|
|
#include <string.h> |
34 |
|
|
#include <imsg.h> |
35 |
|
|
|
36 |
|
|
#include <unistd.h> |
37 |
|
|
#include <err.h> |
38 |
|
|
|
39 |
|
|
#include "parser.h" |
40 |
|
|
#include "npppd_ctl.h" |
41 |
|
|
|
42 |
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
43 |
|
|
|
44 |
|
|
#ifndef nitems |
45 |
|
|
#define nitems(_x) (sizeof(_x) / sizeof(_x[0])) |
46 |
|
|
#endif |
47 |
|
|
|
48 |
|
|
#define NMAX_DISCONNECT 2048 |
49 |
|
|
|
50 |
|
|
static void usage (void); |
51 |
|
|
static void show_clear_session (struct parse_result *, FILE *); |
52 |
|
|
static void monitor_session (struct parse_result *, FILE *); |
53 |
|
|
static void clear_session (u_int[], int, int, FILE *); |
54 |
|
|
static void fprint_who_brief (int, struct npppd_who *, FILE *); |
55 |
|
|
static void fprint_who_packets (int, struct npppd_who *, FILE *); |
56 |
|
|
static void fprint_who_all (int, struct npppd_who *, FILE *); |
57 |
|
|
static const char *peerstr (struct sockaddr *, char *, int); |
58 |
|
|
static const char *humanize_duration (uint32_t, char *, int); |
59 |
|
|
static const char *humanize_bytes (double, char *, int); |
60 |
|
|
static bool filter_match(struct parse_result *, struct npppd_who *); |
61 |
|
|
static int imsg_wait_command_completion (void); |
62 |
|
|
|
63 |
|
|
static int nflag = 0; |
64 |
|
|
static struct imsgbuf ctl_ibuf; |
65 |
|
|
static struct imsg ctl_imsg; |
66 |
|
|
|
67 |
|
|
static void |
68 |
|
|
usage(void) |
69 |
|
|
{ |
70 |
|
|
extern char *__progname; |
71 |
|
|
|
72 |
|
|
fprintf(stderr, |
73 |
|
|
"usage: %s [-n] [-s socket] command [arg ...]\n", __progname); |
74 |
|
|
} |
75 |
|
|
|
76 |
|
|
int |
77 |
|
|
main(int argc, char *argv[]) |
78 |
|
|
{ |
79 |
|
|
int ch, ctlsock = -1; |
80 |
|
|
struct parse_result *result; |
81 |
|
|
struct sockaddr_un sun; |
82 |
|
|
const char *npppd_ctlpath = NPPPD_SOCKET; |
83 |
|
|
extern int optind; |
84 |
|
|
extern char *optarg; |
85 |
|
|
|
86 |
|
|
while ((ch = getopt(argc, argv, "ns:")) != -1) |
87 |
|
|
switch (ch) { |
88 |
|
|
case 'n': |
89 |
|
|
nflag = 1; |
90 |
|
|
break; |
91 |
|
|
case 's': |
92 |
|
|
npppd_ctlpath = optarg; |
93 |
|
|
break; |
94 |
|
|
default: |
95 |
|
|
usage(); |
96 |
|
|
exit(EXIT_FAILURE); |
97 |
|
|
} |
98 |
|
|
|
99 |
|
|
argc -= optind; |
100 |
|
|
argv += optind; |
101 |
|
|
|
102 |
|
|
if ((result = parse(argc, argv)) == NULL) |
103 |
|
|
exit(EXIT_FAILURE); |
104 |
|
|
|
105 |
|
|
if ((ctlsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) |
106 |
|
|
err(EXIT_FAILURE, "socket"); |
107 |
|
|
memset(&sun, 0, sizeof(sun)); |
108 |
|
|
sun.sun_family = AF_UNIX; |
109 |
|
|
strlcpy(sun.sun_path, npppd_ctlpath, sizeof(sun.sun_path)); |
110 |
|
|
if (connect(ctlsock, (struct sockaddr *)&sun, sizeof(sun)) < 0) |
111 |
|
|
err(EXIT_FAILURE, "connect"); |
112 |
|
|
|
113 |
|
|
imsg_init(&ctl_ibuf, ctlsock); |
114 |
|
|
|
115 |
|
|
switch (result->action) { |
116 |
|
|
case SESSION_BRIEF: |
117 |
|
|
case SESSION_PKTS: |
118 |
|
|
case SESSION_ALL: |
119 |
|
|
show_clear_session(result, stdout); |
120 |
|
|
break; |
121 |
|
|
case CLEAR_SESSION: |
122 |
|
|
if (!result->has_ppp_id) |
123 |
|
|
show_clear_session(result, stdout); |
124 |
|
|
else { |
125 |
|
|
u_int ids[1]; |
126 |
|
|
ids[0] = result->ppp_id; |
127 |
|
|
clear_session(ids, 1, 1, stdout); |
128 |
|
|
} |
129 |
|
|
break; |
130 |
|
|
case MONITOR_SESSION: |
131 |
|
|
monitor_session(result, stdout); |
132 |
|
|
break; |
133 |
|
|
case NONE: |
134 |
|
|
break; |
135 |
|
|
} |
136 |
|
|
|
137 |
|
|
exit(EXIT_SUCCESS); |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
static void |
141 |
|
|
show_clear_session(struct parse_result *result, FILE *out) |
142 |
|
|
{ |
143 |
|
|
int i, n, ppp_id_idx; |
144 |
|
|
struct npppd_who_list *res; |
145 |
|
|
u_int ppp_id[NMAX_DISCONNECT]; |
146 |
|
|
|
147 |
|
|
if (imsg_compose(&ctl_ibuf, IMSG_CTL_WHO, 0, 0, -1, NULL, 0) == -1) |
148 |
|
|
err(EXIT_FAILURE, "failed to componse a message\n"); |
149 |
|
|
if (imsg_wait_command_completion() < 0) |
150 |
|
|
errx(EXIT_FAILURE, "failed to get response"); |
151 |
|
|
if (ctl_imsg.hdr.type != IMSG_CTL_OK) |
152 |
|
|
errx(EXIT_FAILURE, "command was fail"); |
153 |
|
|
n = ppp_id_idx = 0; |
154 |
|
|
while (imsg_wait_command_completion() == IMSG_PPP_START) { |
155 |
|
|
res = (struct npppd_who_list *)ctl_imsg.data; |
156 |
|
|
if (ctl_imsg.hdr.len - IMSG_HEADER_SIZE < |
157 |
|
|
offsetof(struct npppd_who_list, |
158 |
|
|
entry[res->entry_count])) { |
159 |
|
|
errx(1, "response size %d is too small for " |
160 |
|
|
"the entry count %d", |
161 |
|
|
(int)(ctl_imsg.hdr.len - IMSG_HEADER_SIZE), |
162 |
|
|
res->entry_count); |
163 |
|
|
} |
164 |
|
|
for (i = 0; i < res->entry_count; i++, n++) { |
165 |
|
|
switch (result->action) { |
166 |
|
|
case SESSION_BRIEF: |
167 |
|
|
fprint_who_brief(n, &res->entry[i], out); |
168 |
|
|
break; |
169 |
|
|
case SESSION_PKTS: |
170 |
|
|
fprint_who_packets(n, &res->entry[i], out); |
171 |
|
|
break; |
172 |
|
|
case SESSION_ALL: |
173 |
|
|
if (filter_match(result, &res->entry[i])) |
174 |
|
|
fprint_who_all(n, &res->entry[i], out); |
175 |
|
|
break; |
176 |
|
|
case CLEAR_SESSION: |
177 |
|
|
if (filter_match(result, &res->entry[i])) { |
178 |
|
|
if (ppp_id_idx < nitems(ppp_id)) |
179 |
|
|
ppp_id[ppp_id_idx] = |
180 |
|
|
res->entry[i].ppp_id; |
181 |
|
|
ppp_id_idx++; |
182 |
|
|
} |
183 |
|
|
break; |
184 |
|
|
default: |
185 |
|
|
warnx("must not reached here"); |
186 |
|
|
abort(); |
187 |
|
|
} |
188 |
|
|
} |
189 |
|
|
if (!res->more_data) |
190 |
|
|
break; |
191 |
|
|
} |
192 |
|
|
if (result->action == CLEAR_SESSION) { |
193 |
|
|
if (ppp_id_idx > nitems(ppp_id)) |
194 |
|
|
warnx( |
195 |
|
|
"Disconnection for %d sessions has been requested, " |
196 |
|
|
"but cannot disconnect only %d sessions because of " |
197 |
|
|
"the implementation limit.", |
198 |
|
|
ppp_id_idx, (int)nitems(ppp_id)); |
199 |
|
|
clear_session(ppp_id, MINIMUM(ppp_id_idx, nitems(ppp_id)), |
200 |
|
|
ppp_id_idx, out); |
201 |
|
|
} |
202 |
|
|
} |
203 |
|
|
|
204 |
|
|
const char *bar = |
205 |
|
|
"------------------------------------------------------------------------\n"; |
206 |
|
|
static void |
207 |
|
|
monitor_session(struct parse_result *result, FILE *out) |
208 |
|
|
{ |
209 |
|
|
int i, n; |
210 |
|
|
struct npppd_who_list *res; |
211 |
|
|
|
212 |
|
|
if (imsg_compose(&ctl_ibuf, IMSG_CTL_MONITOR, 0, 0, -1, NULL, 0) == -1) |
213 |
|
|
err(EXIT_FAILURE, "failed to compose a message"); |
214 |
|
|
if (imsg_wait_command_completion() < 0) |
215 |
|
|
errx(EXIT_FAILURE, "failed to get response"); |
216 |
|
|
if (ctl_imsg.hdr.type != IMSG_CTL_OK) |
217 |
|
|
errx(EXIT_FAILURE, "command was fail"); |
218 |
|
|
do { |
219 |
|
|
if (imsg_wait_command_completion() < 0) |
220 |
|
|
break; |
221 |
|
|
n = 0; |
222 |
|
|
if (ctl_imsg.hdr.type == IMSG_PPP_START || |
223 |
|
|
ctl_imsg.hdr.type == IMSG_PPP_STOP) { |
224 |
|
|
res = (struct npppd_who_list *)ctl_imsg.data; |
225 |
|
|
for (i = 0; i < res->entry_count; i++) { |
226 |
|
|
if (!filter_match(result, &res->entry[i])) |
227 |
|
|
continue; |
228 |
|
|
if (n == 0) |
229 |
|
|
fprintf(out, "PPP %s\n%s", |
230 |
|
|
(ctl_imsg.hdr.type == |
231 |
|
|
IMSG_PPP_START) |
232 |
|
|
? "Started" |
233 |
|
|
: "Stopped", bar); |
234 |
|
|
fprint_who_all(n++, &res->entry[i], out); |
235 |
|
|
} |
236 |
|
|
if (n > 0) |
237 |
|
|
fputs(bar, out); |
238 |
|
|
} else { |
239 |
|
|
warnx("received unknown message type = %d", |
240 |
|
|
ctl_imsg.hdr.type); |
241 |
|
|
break; |
242 |
|
|
} |
243 |
|
|
} while (true); |
244 |
|
|
|
245 |
|
|
return; |
246 |
|
|
} |
247 |
|
|
|
248 |
|
|
static void |
249 |
|
|
fprint_who_brief(int i, struct npppd_who *w, FILE *out) |
250 |
|
|
{ |
251 |
|
|
char buf[BUFSIZ]; |
252 |
|
|
|
253 |
|
|
if (i == 0) |
254 |
|
|
fputs( |
255 |
|
|
"Ppp Id Assigned IPv4 Username Proto Tunnel From\n" |
256 |
|
|
"---------- --------------- -------------------- ----- ------------------------" |
257 |
|
|
"-\n", |
258 |
|
|
out); |
259 |
|
|
fprintf(out, "%10u %-15s %-20s %-5s %s\n", w->ppp_id, |
260 |
|
|
inet_ntoa(w->framed_ip_address), w->username, w->tunnel_proto, |
261 |
|
|
peerstr((struct sockaddr *)&w->tunnel_peer, buf, sizeof(buf))); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
static void |
265 |
|
|
fprint_who_packets(int i, struct npppd_who *w, FILE *out) |
266 |
|
|
{ |
267 |
|
|
if (i == 0) |
268 |
|
|
fputs( |
269 |
|
|
"Ppd Id Username In(Kbytes/pkts/errs) Out(Kbytes/pkts/errs)" |
270 |
|
|
"\n" |
271 |
|
|
"---------- -------------------- ----------------------- ----------------------" |
272 |
|
|
"-\n", |
273 |
|
|
out); |
274 |
|
|
fprintf(out, "%10u %-20s %9.1f %7u %5u %9.1f %7u %5u\n", w->ppp_id, |
275 |
|
|
w->username, |
276 |
|
|
(double)w->ibytes/1024, w->ipackets, w->ierrors, |
277 |
|
|
(double)w->obytes/1024, w->opackets, w->oerrors); |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
static void |
281 |
|
|
fprint_who_all(int i, struct npppd_who *w, FILE *out) |
282 |
|
|
{ |
283 |
|
|
struct tm tm; |
284 |
|
|
char ibytes_buf[48], obytes_buf[48], peer_buf[48], time_buf[48]; |
285 |
|
|
char dur_buf[48]; |
286 |
|
|
|
287 |
|
|
localtime_r(&w->time, &tm); |
288 |
|
|
strftime(time_buf, sizeof(time_buf), "%Y/%m/%d %T", &tm); |
289 |
|
|
if (i != 0) |
290 |
|
|
fputs("\n", out); |
291 |
|
|
|
292 |
|
|
fprintf(out, |
293 |
|
|
"Ppp Id = %u\n" |
294 |
|
|
" Ppp Id : %u\n" |
295 |
|
|
" Username : %s\n" |
296 |
|
|
" Realm Name : %s\n" |
297 |
|
|
" Concentrated Interface : %s\n" |
298 |
|
|
" Assigned IPv4 Address : %s\n" |
299 |
|
|
" MRU : %u\n" |
300 |
|
|
" Tunnel Protocol : %s\n" |
301 |
|
|
" Tunnel From : %s\n" |
302 |
|
|
" Start Time : %s\n" |
303 |
|
|
" Elapsed Time : %lu sec %s\n" |
304 |
|
|
" Input Bytes : %llu%s\n" |
305 |
|
|
" Input Packets : %lu\n" |
306 |
|
|
" Input Errors : %lu (%.1f%%)\n" |
307 |
|
|
" Output Bytes : %llu%s\n" |
308 |
|
|
" Output Packets : %lu\n" |
309 |
|
|
" Output Errors : %lu (%.1f%%)\n", |
310 |
|
|
w->ppp_id, w->ppp_id, w->username, w->rlmname, w->ifname, |
311 |
|
|
inet_ntoa(w->framed_ip_address), (u_int)w->mru, w->tunnel_proto, |
312 |
|
|
peerstr((struct sockaddr *)&w->tunnel_peer, peer_buf, |
313 |
|
|
sizeof(peer_buf)), time_buf, |
314 |
|
|
(unsigned long)w->duration_sec, |
315 |
|
|
humanize_duration(w->duration_sec, dur_buf, sizeof(dur_buf)), |
316 |
|
|
(unsigned long long)w->ibytes, |
317 |
|
|
humanize_bytes((double)w->ibytes, ibytes_buf, sizeof(ibytes_buf)), |
318 |
|
|
(unsigned long)w->ipackets, |
319 |
|
|
(unsigned long)w->ierrors, |
320 |
|
|
((w->ipackets + w->ierrors) <= 0) |
321 |
|
|
? 0.0 : (100.0 * w->ierrors) / (w->ierrors + w->ipackets), |
322 |
|
|
(unsigned long long)w->obytes, |
323 |
|
|
humanize_bytes((double)w->obytes, obytes_buf, sizeof(obytes_buf)), |
324 |
|
|
(unsigned long)w->opackets, |
325 |
|
|
(unsigned long)w->oerrors, |
326 |
|
|
((w->opackets + w->oerrors) <= 0) |
327 |
|
|
? 0.0 : (100.0 * w->oerrors) / (w->oerrors + w->opackets)); |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
/*********************************************************************** |
331 |
|
|
* clear session |
332 |
|
|
***********************************************************************/ |
333 |
|
|
static void |
334 |
|
|
clear_session(u_int ppp_id[], int ppp_id_count, int total, FILE *out) |
335 |
|
|
{ |
336 |
|
|
int succ, fail, i, n, nmax; |
337 |
|
|
struct iovec iov[2]; |
338 |
|
|
struct npppd_disconnect_request req; |
339 |
|
|
struct npppd_disconnect_response *res; |
340 |
|
|
|
341 |
|
|
succ = fail = 0; |
342 |
|
|
if (ppp_id_count > 0) { |
343 |
|
|
nmax = (MAX_IMSGSIZE - IMSG_HEADER_SIZE - |
344 |
|
|
offsetof(struct npppd_disconnect_request, ppp_id[0])) / |
345 |
|
|
sizeof(u_int); |
346 |
|
|
for (i = 0; i < ppp_id_count; i += n) { |
347 |
|
|
n = MINIMUM(nmax, ppp_id_count - i); |
348 |
|
|
req.count = n; |
349 |
|
|
iov[0].iov_base = &req; |
350 |
|
|
iov[0].iov_len = offsetof( |
351 |
|
|
struct npppd_disconnect_request, ppp_id[0]); |
352 |
|
|
iov[1].iov_base = &ppp_id[i]; |
353 |
|
|
iov[1].iov_len = sizeof(u_int) * n; |
354 |
|
|
|
355 |
|
|
if (imsg_composev(&ctl_ibuf, IMSG_CTL_DISCONNECT, 0, 0, |
356 |
|
|
-1, iov, 2) == -1) |
357 |
|
|
err(EXIT_FAILURE, |
358 |
|
|
"Failed to compose a message"); |
359 |
|
|
if (imsg_wait_command_completion() < 0) |
360 |
|
|
errx(EXIT_FAILURE, "failed to get response"); |
361 |
|
|
if (ctl_imsg.hdr.type != IMSG_CTL_OK) |
362 |
|
|
errx(EXIT_FAILURE, |
363 |
|
|
"Command was fail: msg type = %d", |
364 |
|
|
ctl_imsg.hdr.type); |
365 |
|
|
if (ctl_imsg.hdr.len - IMSG_HEADER_SIZE < |
366 |
|
|
sizeof(struct npppd_disconnect_response)) |
367 |
|
|
err(EXIT_FAILURE, "response is corrupted"); |
368 |
|
|
res = (struct npppd_disconnect_response *)ctl_imsg.data; |
369 |
|
|
succ += res->count; |
370 |
|
|
} |
371 |
|
|
fail = total - succ; |
372 |
|
|
} |
373 |
|
|
if (succ > 0) |
374 |
|
|
fprintf(out, "Successfully disconnected %d session%s.\n", |
375 |
|
|
succ, (succ > 1)? "s" : ""); |
376 |
|
|
if (fail > 0) |
377 |
|
|
fprintf(out, "Failed to disconnect %d session%s.\n", |
378 |
|
|
fail, (fail > 1)? "s" : ""); |
379 |
|
|
if (succ == 0 && fail == 0) |
380 |
|
|
fprintf(out, "No session to disconnect.\n"); |
381 |
|
|
} |
382 |
|
|
|
383 |
|
|
/*********************************************************************** |
384 |
|
|
* common functions |
385 |
|
|
***********************************************************************/ |
386 |
|
|
static bool |
387 |
|
|
filter_match(struct parse_result *result, struct npppd_who *who) |
388 |
|
|
{ |
389 |
|
|
if (result->has_ppp_id && result->ppp_id != who->ppp_id) |
390 |
|
|
return (false); |
391 |
|
|
|
392 |
|
|
switch (result->address.ss_family) { |
393 |
|
|
case AF_INET: |
394 |
|
|
if (((struct sockaddr_in *)&result->address)->sin_addr. |
395 |
|
|
s_addr != who->framed_ip_address.s_addr) |
396 |
|
|
return (false); |
397 |
|
|
break; |
398 |
|
|
case AF_INET6: |
399 |
|
|
/* npppd doesn't support IPv6 yet */ |
400 |
|
|
return (false); |
401 |
|
|
} |
402 |
|
|
|
403 |
|
|
if (result->interface != NULL && |
404 |
|
|
strcmp(result->interface, who->ifname) != 0) |
405 |
|
|
return (false); |
406 |
|
|
|
407 |
|
|
if (result->protocol != PROTO_UNSPEC && |
408 |
|
|
result->protocol != parse_protocol(who->tunnel_proto) ) |
409 |
|
|
return (false); |
410 |
|
|
|
411 |
|
|
if (result->realm != NULL && strcmp(result->realm, who->rlmname) != 0) |
412 |
|
|
return (false); |
413 |
|
|
|
414 |
|
|
if (result->username != NULL && |
415 |
|
|
strcmp(result->username, who->username) != 0) |
416 |
|
|
return (false); |
417 |
|
|
|
418 |
|
|
return (true); |
419 |
|
|
} |
420 |
|
|
|
421 |
|
|
static const char * |
422 |
|
|
peerstr(struct sockaddr *sa, char *buf, int lbuf) |
423 |
|
|
{ |
424 |
|
|
int niflags, hasserv; |
425 |
|
|
char hoststr[NI_MAXHOST], servstr[NI_MAXSERV]; |
426 |
|
|
|
427 |
|
|
niflags = hasserv = 0; |
428 |
|
|
if (nflag) |
429 |
|
|
niflags |= NI_NUMERICHOST; |
430 |
|
|
if (sa->sa_family == AF_INET || sa->sa_family ==AF_INET6) { |
431 |
|
|
hasserv = 1; |
432 |
|
|
niflags |= NI_NUMERICSERV; |
433 |
|
|
} |
434 |
|
|
|
435 |
|
|
if (sa->sa_family == AF_LINK) |
436 |
|
|
snprintf(hoststr, sizeof(hoststr), |
437 |
|
|
"%02x:%02x:%02x:%02x:%02x:%02x", |
438 |
|
|
LLADDR((struct sockaddr_dl *)sa)[0] & 0xff, |
439 |
|
|
LLADDR((struct sockaddr_dl *)sa)[1] & 0xff, |
440 |
|
|
LLADDR((struct sockaddr_dl *)sa)[2] & 0xff, |
441 |
|
|
LLADDR((struct sockaddr_dl *)sa)[3] & 0xff, |
442 |
|
|
LLADDR((struct sockaddr_dl *)sa)[4] & 0xff, |
443 |
|
|
LLADDR((struct sockaddr_dl *)sa)[5] & 0xff); |
444 |
|
|
else |
445 |
|
|
getnameinfo(sa, sa->sa_len, hoststr, sizeof(hoststr), servstr, |
446 |
|
|
sizeof(servstr), niflags); |
447 |
|
|
|
448 |
|
|
strlcpy(buf, hoststr, lbuf); |
449 |
|
|
if (hasserv) { |
450 |
|
|
strlcat(buf, ":", lbuf); |
451 |
|
|
strlcat(buf, servstr, lbuf); |
452 |
|
|
} |
453 |
|
|
|
454 |
|
|
return (buf); |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
static const char * |
458 |
|
|
humanize_duration(uint32_t sec, char *buf, int lbuf) |
459 |
|
|
{ |
460 |
|
|
char fbuf[128]; |
461 |
|
|
int hour, min; |
462 |
|
|
|
463 |
|
|
hour = sec / (60 * 60); |
464 |
|
|
min = sec / 60; |
465 |
|
|
min %= 60; |
466 |
|
|
|
467 |
|
|
if (lbuf <= 0) |
468 |
|
|
return (buf); |
469 |
|
|
buf[0] = '\0'; |
470 |
|
|
if (hour || min) { |
471 |
|
|
strlcat(buf, "(", lbuf); |
472 |
|
|
if (hour) { |
473 |
|
|
snprintf(fbuf, sizeof(fbuf), |
474 |
|
|
"%d hour%s", hour, (hour > 1)? "s" : ""); |
475 |
|
|
strlcat(buf, fbuf, lbuf); |
476 |
|
|
} |
477 |
|
|
if (hour && min) |
478 |
|
|
strlcat(buf, " and ", lbuf); |
479 |
|
|
if (min) { |
480 |
|
|
snprintf(fbuf, sizeof(fbuf), |
481 |
|
|
"%d minute%s", min, (min > 1)? "s" : ""); |
482 |
|
|
strlcat(buf, fbuf, lbuf); |
483 |
|
|
} |
484 |
|
|
strlcat(buf, ")", lbuf); |
485 |
|
|
} |
486 |
|
|
|
487 |
|
|
return (buf); |
488 |
|
|
} |
489 |
|
|
|
490 |
|
|
static const char * |
491 |
|
|
humanize_bytes(double val, char *buf, int lbuf) |
492 |
|
|
{ |
493 |
|
|
if (lbuf <= 0) |
494 |
|
|
return (buf); |
495 |
|
|
|
496 |
|
|
if (val >= 1000 * 1024 * 1024) |
497 |
|
|
snprintf(buf, lbuf, " (%.1f GB)", |
498 |
|
|
(double)val / (1024 * 1024 * 1024)); |
499 |
|
|
else if (val >= 1000 * 1024) |
500 |
|
|
snprintf(buf, lbuf, " (%.1f MB)", (double)val / (1024 * 1024)); |
501 |
|
|
else if (val >= 1000) |
502 |
|
|
snprintf(buf, lbuf, " (%.1f KB)", (double)val / 1024); |
503 |
|
|
else |
504 |
|
|
buf[0] = '\0'; |
505 |
|
|
|
506 |
|
|
return (buf); |
507 |
|
|
} |
508 |
|
|
|
509 |
|
|
static int |
510 |
|
|
imsg_wait_command_completion(void) |
511 |
|
|
{ |
512 |
|
|
int n; |
513 |
|
|
|
514 |
|
|
while (ctl_ibuf.w.queued) |
515 |
|
|
if (msgbuf_write(&ctl_ibuf.w) <= 0 && errno != EAGAIN) |
516 |
|
|
return (-1); |
517 |
|
|
do { |
518 |
|
|
if ((n = imsg_get(&ctl_ibuf, &ctl_imsg)) == -1) |
519 |
|
|
return (-1); |
520 |
|
|
if (n != 0) |
521 |
|
|
break; |
522 |
|
|
if (((n = imsg_read(&ctl_ibuf)) == -1 && errno != EAGAIN) || |
523 |
|
|
n == 0) |
524 |
|
|
return (-1); |
525 |
|
|
} while (1); |
526 |
|
|
|
527 |
|
|
return (ctl_imsg.hdr.type); |
528 |
|
|
} |