1 /* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
2 *
3 * distcc -- A simple distributed compiler system
4 *
5 * Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
6 * Copyright 2007 Google Inc.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 * USA.
22 */
23
24 #include <config.h>
25
26 #include <dirent.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <netdb.h>
35 #include <ctype.h>
36
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/un.h>
40 #include <sys/socket.h>
41
42 #include <netinet/in.h>
43
44 #ifdef HAVE_SYS_RESOURCE_H
45 #include <sys/resource.h>
46 #endif
47
48 #ifdef HAVE_SYS_LOADAVG_H
49 #include <sys/loadavg.h>
50 #endif
51
52 #include <sys/param.h>
53
54 #include "distcc.h"
55 #include "trace.h"
56 #include "util.h"
57 #include "exitcode.h"
58 #include "snprintf.h"
59
60 /* I will make a man more precious than fine
61 * gold; even a man than the golden wedge of
62 * Ophir.
63 * -- Isaiah 13:12 */
64
65
dcc_exit(int exitcode)66 void dcc_exit(int exitcode)
67 {
68 struct rusage self_ru, children_ru;
69
70 if (getrusage(RUSAGE_SELF, &self_ru)) {
71 rs_log_warning("getrusage(RUSAGE_SELF) failed: %s", strerror(errno));
72 memset(&self_ru, 0, sizeof self_ru);
73 }
74 if (getrusage(RUSAGE_CHILDREN, &children_ru)) {
75 rs_log_warning("getrusage(RUSAGE_CHILDREN) failed: %s", strerror(errno));
76 memset(&children_ru, 0, sizeof children_ru);
77 }
78
79 /* NB fields must match up for microseconds */
80 rs_log(RS_LOG_INFO,
81 "exit: code %d; self: %d.%06d user %d.%06d sys; children: %d.%06d user %d.%06d sys",
82 exitcode,
83 (int) self_ru.ru_utime.tv_sec, (int) self_ru.ru_utime.tv_usec,
84 (int) self_ru.ru_stime.tv_sec, (int) self_ru.ru_stime.tv_usec,
85 (int) children_ru.ru_utime.tv_sec, (int) children_ru.ru_utime.tv_usec,
86 (int) children_ru.ru_stime.tv_sec, (int) children_ru.ru_stime.tv_usec);
87
88 exit(exitcode);
89 }
90
91
str_endswith(const char * tail,const char * tiger)92 int str_endswith(const char *tail, const char *tiger)
93 {
94 size_t len_tail = strlen(tail);
95 size_t len_tiger = strlen(tiger);
96
97 if (len_tail > len_tiger)
98 return 0;
99
100 return !strcmp(tiger + len_tiger - len_tail, tail);
101 }
102
103
str_startswith(const char * head,const char * worm)104 int str_startswith(const char *head, const char *worm)
105 {
106 return !strncmp(head, worm, strlen(head));
107 }
108
109
110
111 /**
112 * Skim through NULL-terminated @p argv, looking for @p s.
113 **/
argv_contains(char ** argv,const char * s)114 int argv_contains(char **argv, const char *s)
115 {
116 while (*argv) {
117 if (!strcmp(*argv, s))
118 return 1;
119 argv++;
120 }
121 return 0;
122 }
123
124
125 /**
126 * Redirect a file descriptor into (or out of) a file.
127 *
128 * Used, for example, to catch compiler error messages into a
129 * temporary file.
130 **/
dcc_redirect_fd(int fd,const char * fname,int mode)131 int dcc_redirect_fd(int fd, const char *fname, int mode)
132 {
133 int newfd;
134
135 /* ignore errors */
136 close(fd);
137
138 newfd = open(fname, mode, 0666);
139 if (newfd == -1) {
140 rs_log_crit("failed to reopen fd%d onto %s: %s",
141 fd, fname, strerror(errno));
142 return EXIT_IO_ERROR;
143 } else if (newfd != fd) {
144 rs_log_crit("oops, reopened fd%d onto fd%d?", fd, newfd);
145 return EXIT_IO_ERROR;
146 }
147
148 return 0;
149 }
150
151
152
dcc_gethostname(void)153 char *dcc_gethostname(void)
154 {
155 static char myname[100] = "\0";
156
157 if (!myname[0]) {
158 if (gethostname(myname, sizeof myname - 1) == -1)
159 strcpy(myname, "UNKNOWN");
160 }
161
162 return myname;
163 }
164
165
166 /**
167 * Look up a boolean environment option, which must be either "0" or
168 * "1". The default, if it's not set or is empty, is @p default.
169 **/
dcc_getenv_bool(const char * name,int default_value)170 int dcc_getenv_bool(const char *name, int default_value)
171 {
172 const char *e;
173
174 e = getenv(name);
175 if (!e || !*e)
176 return default_value;
177 if (!strcmp(e, "1"))
178 return 1;
179 else if (!strcmp(e, "0"))
180 return 0;
181 else
182 return default_value;
183 }
184
185
186 #define IS_LEGAL_DOMAIN_CHAR(c) (isalnum((uint8_t)c) || ((c) == '-') || ((c) == '.'))
187
188 /* Copy domain part of hostname to static buffer.
189 * If hostname has no domain part, returns -1.
190 * If domain lookup fails, returns -1.
191 * Otherwise places pointer to domain in *domain_name and returns 0.
192 *
193 * This should yield the same result as the linux command
194 * 'dnsdomainname' or 'hostname -d'.
195 **/
dcc_get_dns_domain(const char ** domain_name)196 int dcc_get_dns_domain(const char **domain_name)
197 {
198 #if 0 /* Too expensive */
199
200 static char host_name[1024];
201 struct hostent *h;
202 int ret;
203
204 ret = gethostname(host_name, sizeof(host_name));
205 if (ret != 0)
206 return -1;
207
208 h = gethostbyname(host_name);
209 if (h == NULL) {
210 rs_log_error("failed to look up self \"%s\": %s", host_name,
211 hstrerror(h_errno));
212 return -1;
213 }
214
215 strncpy(host_name, h->h_name, sizeof(host_name) - 1);
216 host_name[sizeof(host_name) - 1] = '\0';
217 *domain_name = strchr(h->h_name, '.');
218
219 #else /* cheaper */
220 const char *envh, *envh2;
221 int i;
222 const int MAXDOMAINLEN = 512;
223
224 /* Kludge for speed: Try to retrieve FQDN from environment.
225 * This can save many milliseconds on a network that's busy and lossy
226 * (glibc retries DNS operations very slowly).
227 */
228
229 /* Solaris, BSD tend to put it in HOST.
230 * (Some flavors of Linux put the non-qualified hostname in HOST,
231 * so ignore this if it doesn't have a dot in it.)
232 */
233 envh = getenv("HOST");
234 if (envh && !strchr(envh, '.'))
235 envh = NULL;
236
237 /* Some flavors of Linux put the FQDN in HOSTNAME when
238 * logged in interactively, but not when ssh'd in noninteractively.
239 * Ubuntu's bash puts it in HOSTNAME but doesn't export it!
240 */
241 envh2 = getenv("HOSTNAME");
242 if (envh2 && !strchr(envh2, '.'))
243 envh2 = NULL;
244
245 /* Pick the 'better' of the two. Longer is usually better. */
246 if (envh2 && (!envh || (strlen(envh) < strlen(envh2))))
247 envh = envh2;
248
249 /* If the above didn't work out, fall back to the real way. */
250 if (!envh || !strchr(envh, '.')) {
251 static char host_name[1024];
252 struct hostent *h;
253 int ret;
254
255 ret = gethostname(host_name, sizeof(host_name));
256 if (ret != 0)
257 return -1;
258
259 /* If hostname has a dot in it, assume it's the DNS address */
260 if (!strchr(host_name, '.')) {
261 /* Otherwise ask DNS what our full hostname is */
262 h = gethostbyname(host_name);
263 if (h == NULL) {
264 rs_log_error("failed to look up self \"%s\": %s", host_name,
265 hstrerror(h_errno));
266 return -1;
267 }
268 strncpy(host_name, h->h_name, sizeof(host_name) - 1);
269 host_name[sizeof(host_name) - 1] = '\0';
270 }
271 envh = host_name;
272 }
273
274 /* validate to avoid possible errors from bad chars or huge value */
275 for (i=0; envh[i] != '\0'; i++) {
276 if (i > MAXDOMAINLEN || !IS_LEGAL_DOMAIN_CHAR(envh[i])) {
277 rs_log_error("HOST/HOSTNAME present in environment but illegal: '%s'", envh);
278 return -1;
279 }
280 }
281 *domain_name = strchr(envh, '.');
282 #endif
283
284 if (*domain_name == NULL)
285 return -1;
286
287 (*domain_name)++;
288 /* Return 0 on success, or -1 if the domain name is illegal, e.g. empty */
289 return ((*domain_name)[0] == '\0') ? -1 : 0;
290 }
291
292
293
294 /**
295 * Set the `FD_CLOEXEC' flag of DESC if VALUE is nonzero,
296 * or clear the flag if VALUE is 0.
297 *
298 * From the GNU C Library examples.
299 *
300 * @returns 0 on success, or -1 on error with `errno' set.
301 **/
set_cloexec_flag(int desc,int value)302 int set_cloexec_flag (int desc, int value)
303 {
304 int oldflags = fcntl (desc, F_GETFD, 0);
305 /* If reading the flags failed, return error indication now. */
306 if (oldflags < 0)
307 return oldflags;
308 /* Set just the flag we want to set. */
309 if (value != 0)
310 oldflags |= FD_CLOEXEC;
311 else
312 oldflags &= ~FD_CLOEXEC;
313 /* Store modified flag word in the descriptor. */
314 return fcntl (desc, F_SETFD, oldflags);
315 }
316
317
318 /**
319 * Ignore or unignore SIGPIPE.
320 *
321 * The server and child ignore it, because distcc code wants to see
322 * EPIPE errors if something goes wrong. However, for invoked
323 * children it is set back to the default value, because they may not
324 * handle the error properly.
325 **/
dcc_ignore_sigpipe(int val)326 int dcc_ignore_sigpipe(int val)
327 {
328 if (signal(SIGPIPE, val ? SIG_IGN : SIG_DFL) == SIG_ERR) {
329 rs_log_warning("signal(SIGPIPE, %s) failed: %s",
330 val ? "ignore" : "default",
331 strerror(errno));
332 return EXIT_DISTCC_FAILED;
333 }
334 return 0;
335 }
336
337 /**
338 * Search through the $PATH looking for a directory containing a file called
339 * @p compiler_name, which is a symbolic link containing the string "distcc".
340 *
341 * Trim the path to just after the *last* such directory.
342 *
343 * If we find a distcc masquerade dir on the PATH, remove all the dirs up
344 * to that point.
345 **/
dcc_trim_path(const char * compiler_name)346 int dcc_trim_path(const char *compiler_name)
347 {
348 const char *envpath, *newpath, *p, *n;
349 char linkbuf[MAXPATHLEN], *buf;
350 struct stat sb;
351 size_t len;
352
353 if (!(envpath = getenv("PATH"))) {
354 rs_trace("PATH seems not to be defined");
355 return 0;
356 }
357
358 rs_trace("original PATH %s", envpath);
359 rs_trace("looking for \"%s\"", compiler_name);
360
361 /* Allocate a buffer that will let us append "/cc" onto any PATH
362 * element, even if there is only one item in the PATH. */
363 if (!(buf = malloc(strlen(envpath)+1+strlen(compiler_name)+1))) {
364 rs_log_error("failed to allocate buffer for PATH munging");
365 return EXIT_OUT_OF_MEMORY;
366 }
367
368 for (n = p = envpath, newpath = NULL; *n; p = n) {
369 n = strchr(p, ':');
370 if (n)
371 len = n++ - p;
372 else {
373 len = strlen(p);
374 n = p + len;
375 }
376 strncpy(buf, p, len);
377
378 sprintf(buf + len, "/%s", compiler_name);
379 if (lstat(buf, &sb) == -1)
380 continue; /* ENOENT, EACCESS, etc */
381 if (!S_ISLNK(sb.st_mode))
382 break;
383 if ((len = readlink(buf, linkbuf, sizeof linkbuf)) <= 0)
384 continue;
385 linkbuf[len] = '\0';
386 if (strstr(linkbuf, "distcc")) {
387 /* Set newpath to the part of the PATH past our match. */
388 newpath = n;
389 }
390 }
391
392 if (newpath) {
393 int ret = dcc_set_path(newpath);
394 if (ret)
395 return ret;
396 } else
397 rs_trace("not modifying PATH");
398
399 free(buf);
400 return 0;
401 }
402
403 /* Set the PATH environment variable to the indicated value. */
dcc_set_path(const char * newpath)404 int dcc_set_path(const char *newpath)
405 {
406 char *buf;
407
408 if (asprintf(&buf, "PATH=%s", newpath) <= 0 || !buf) {
409 rs_log_error("failed to allocate buffer for new PATH");
410 return EXIT_OUT_OF_MEMORY;
411 }
412 rs_trace("setting %s", buf);
413 if (putenv(buf) < 0) {
414 rs_log_error("putenv PATH failed");
415 return EXIT_FAILURE;
416 }
417 /* We must leave "buf" allocated. */
418 return 0;
419 }
420
421 /* Return the supplied path with the current-working directory prefixed (if
422 * needed) and all "dir/.." references removed. Supply path_len if you want
423 * to use only a substring of the path string, otherwise make it 0. */
dcc_abspath(const char * path,int path_len)424 char *dcc_abspath(const char *path, int path_len)
425 {
426 static char buf[MAXPATHLEN];
427 unsigned len;
428 char *p, *slash;
429
430 if (*path == '/')
431 len = 0;
432 else {
433 char *ret;
434 #ifdef HAVE_GETCWD
435 ret = getcwd(buf, sizeof buf);
436 if (ret == NULL) {
437 rs_log_crit("getcwd failed: %s", strerror(errno));
438 }
439 #else
440 ret = getwd(buf);
441 if (ret == NULL) {
442 rs_log_crit("getwd failed: %s", strerror(errno));
443 }
444 #endif
445 len = strlen(buf);
446 if (len >= sizeof buf) {
447 rs_log_crit("getwd overflowed in dcc_abspath()");
448 }
449 buf[len++] = '/';
450 }
451 if (path_len <= 0)
452 path_len = strlen(path);
453 if (path_len >= 2 && *path == '.' && path[1] == '/') {
454 path += 2;
455 path_len -= 2;
456 }
457 if (len + (unsigned)path_len >= sizeof buf) {
458 rs_log_error("path overflowed in dcc_abspath()");
459 exit(EXIT_OUT_OF_MEMORY);
460 }
461 strncpy(buf + len, path, path_len);
462 buf[len + path_len] = '\0';
463 for (p = buf+len-(len > 0); (p = strstr(p, "/../")) != NULL; p = slash) {
464 *p = '\0';
465 if (!(slash = strrchr(buf, '/')))
466 slash = p;
467 strcpy(slash, p+3);
468 }
469 return buf;
470 }
471
472 /* Return -1 if a < b, 0 if a == b, and 1 if a > b */
dcc_timecmp(struct timeval a,struct timeval b)473 int dcc_timecmp(struct timeval a, struct timeval b) {
474 if (a.tv_sec < b.tv_sec) {
475 return -1;
476 } else if (a.tv_sec > b.tv_sec) {
477 return 1;
478 } else if (a.tv_usec < b.tv_usec) {
479 /* at this point their tv_sec must be the same */
480 return -1;
481 } else if (a.tv_usec > b.tv_usec) {
482 return 1;
483 } else {
484 /* they must be equal */
485 return 0;
486 }
487 }
488
489
490 /* Return the current number of running processes. */
dcc_getcurrentload(void)491 int dcc_getcurrentload(void) {
492 #if defined(linux)
493 double stats[3];
494 int running;
495 int total;
496 int last_pid;
497 int retval;
498
499 FILE *f = fopen("/proc/loadavg", "r");
500 if (NULL == f)
501 return -1;
502
503 retval = fscanf(f, "%lf %lf %lf %d/%d %d", &stats[0], &stats[1], &stats[2],
504 &running, &total, &last_pid);
505 fclose(f);
506
507 if (6 != retval)
508 return -1;
509
510 return running;
511 #else
512 return -1;
513 #endif
514 }
515
516 /**
517 * Wrapper for getloadavg() that tries to return all 3 samples, and reports
518 * -1 for those samples that are not available.
519 *
520 * Averages are over the last 1, 5, and 15 minutes, respectively.
521 **/
dcc_getloadavg(double loadavg[3])522 void dcc_getloadavg(double loadavg[3]) {
523 int num;
524 int i;
525
526 #if defined(HAVE_GETLOADAVG)
527 num = getloadavg(loadavg, 3);
528 #else
529 num = 0;
530 #endif
531
532 /* If getloadavg() didn't return 3 we want to fill
533 * in the invalid elements with -1 */
534 if (num < 0)
535 num = 0;
536
537 for (i=num; i < 3; ++i)
538 loadavg[i] = -1;
539 }
540
541
542 /**
543 * Duplicate the part of the string @p psrc up to a character in @p sep
544 * (or end of string), storing the result in @p pdst. @p psrc is updated to
545 * point to the terminator. (If the terminator is not found it will
546 * therefore point to \0.
547 *
548 * If there is no more string, then @p pdst is instead set to NULL, no
549 * memory is allocated, and @p psrc is not advanced.
550 **/
dcc_dup_part(const char ** psrc,char ** pdst,const char * sep)551 int dcc_dup_part(const char **psrc, char **pdst, const char *sep)
552 {
553 size_t len;
554
555 len = strcspn(*psrc, sep);
556 if (len == 0) {
557 *pdst = NULL;
558 } else {
559 if (!(*pdst = malloc(len + 1))) {
560 rs_log_error("failed to allocate string duplicate: %d", (int) len);
561 return EXIT_OUT_OF_MEMORY;
562 }
563 strncpy(*pdst, *psrc, len);
564 (*pdst)[len] = '\0';
565 (*psrc) += len;
566 }
567
568 return 0;
569 }
570
571
572
dcc_remove_if_exists(const char * fname)573 int dcc_remove_if_exists(const char *fname)
574 {
575 if (unlink(fname) && errno != ENOENT) {
576 rs_log_warning("failed to unlink %s: %s", fname,
577 strerror(errno));
578 return EXIT_IO_ERROR;
579 }
580 return 0;
581 }
582
dcc_which(const char * command,char ** out)583 int dcc_which(const char *command, char **out)
584 {
585 char *loc = NULL, *_loc, *path, *t;
586 int ret;
587
588 path = getenv("PATH");
589 if (!path)
590 return -ENOENT;
591 do {
592 if (strstr(path, "distcc"))
593 continue;
594 /* emulate strchrnul() */
595 t = strchr(path, ':');
596 if (!t)
597 t = path + strlen(path);
598 _loc = realloc(loc, t - path + 1 + strlen(command) + 1);
599 if (!_loc) {
600 free(loc);
601 return -ENOMEM;
602 }
603 loc = _loc;
604 strncpy(loc, path, t - path);
605 loc[t - path] = '\0';
606 strcat(loc, "/");
607 strcat(loc, command);
608 ret = access(loc, X_OK);
609 if (ret < 0)
610 continue;
611 *out = loc;
612 return 0;
613 } while ((path = strchr(path, ':') + 1));
614 return -ENOENT;
615 }
616
617 /* Returns the number of processes in state D, the max non-cc/c++ RSS in kb and
618 * the max RSS program's name */
dcc_get_proc_stats(int * num_D,int * max_RSS,char ** max_RSS_name)619 void dcc_get_proc_stats(int *num_D, int *max_RSS, char **max_RSS_name) {
620 #if defined(linux)
621 DIR *proc = opendir("/proc");
622 struct dirent *procsubdir;
623 static int pagesize = -1;
624 static char RSS_name[1024];
625 char statfile[1024];
626 FILE *f;
627 char name[1024];
628 char state;
629 int pid;
630 int rss_size;
631 int l;
632 char *c;
633 int isCC;
634
635 /* If this doesn't cut it for you, see how CVS does it:
636 * http://savannah.nongnu.org/cgi-bin/viewcvs/cvs/ccvs/lib/getpagesize.h */
637 if (pagesize == -1) {
638 #if HAVE_GETPAGESIZE
639 pagesize = getpagesize();
640 #else
641 pagesize = 8192;
642 #endif
643 }
644
645 *num_D = 0;
646 *max_RSS = 0;
647 *max_RSS_name = RSS_name;
648 RSS_name[0] = 0;
649
650 while ((procsubdir = readdir(proc)) != NULL) {
651 if (sscanf(procsubdir->d_name, "%d", &pid) != 1)
652 continue;
653
654 strcpy(statfile, "/proc/");
655 strcat(statfile, procsubdir->d_name);
656 strcat(statfile, "/stat");
657
658 f = fopen(statfile, "r");
659 if (f == NULL)
660 continue;
661
662 if (fscanf(f, "%*d %s %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d",
663 name, &state, &rss_size) != 3) {
664 fclose(f);
665 continue;
666 }
667
668 rss_size = (rss_size * pagesize) / 1024; /* get rss_size in KB */
669
670 if (state == 'D') {
671 (*num_D)++;
672 }
673
674 l = strlen(RSS_name);
675 c = RSS_name;
676
677 /* check for .*{++,cc} */
678 isCC = (l >= 2) && ((c[l-1] == 'c' && c[l-2] == 'c')
679 || (c[l-1] == '+' && c[l-2] == '+'));
680 if ((rss_size > *max_RSS) && !isCC) {
681 *max_RSS = rss_size;
682 strncpy(RSS_name, name, 1024);
683 }
684
685 fclose(f);
686 }
687
688 closedir(proc);
689 #else
690 static char RSS_name[] = "none";
691 *num_D = -1;
692 *max_RSS = -1;
693 *max_RSS_name = RSS_name;
694 #endif
695 }
696
697
698 /* Returns the number of sector read/writes since boot */
dcc_get_disk_io_stats(int * n_reads,int * n_writes)699 void dcc_get_disk_io_stats(int *n_reads, int *n_writes) {
700 #if defined(linux)
701 int retval;
702 int kernel26 = 1;
703 FILE *f;
704 int reads, writes, minor;
705 char dev[100];
706 char tmp[1024];
707
708 *n_reads = 0;
709 *n_writes = 0;
710
711 f = fopen("/proc/diskstats", "r");
712 if (f == NULL) {
713 if (errno != ENOENT)
714 return;
715
716 /* /proc/diskstats does not exist. probably a 2.4 kernel, so try reading
717 * /proc/partitions */
718 f = fopen("/proc/partitions", "r");
719 if (f == NULL)
720 return;
721 kernel26 = 0;
722 }
723
724 if (!kernel26) /* blast away 2 header lines in /proc/partitions */ {
725 retval = fscanf(f,
726 "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s");
727 if (retval == EOF) {
728 fclose(f);
729 return;
730 }
731 }
732
733 while (1) {
734 if (kernel26)
735 retval = fscanf(f, " %*d %d %s", &minor, dev);
736 else
737 retval = fscanf(f, " %*d %d %*d %s", &minor, dev);
738
739 if (retval == EOF || retval != 2)
740 break;
741
742 if (minor % 64 == 0
743 && ((dev[0] == 'h' && dev[1] == 'd' && dev[2] == 'a')
744 || (dev[0] == 's' && dev[1] == 'd' && dev[2] == 'a'))) {
745 /* disk stats */
746 retval = fscanf(f, " %*d %*d %d %*d %*d %*d %d %*d %*d %*d %*d",
747 &reads, &writes);
748 if (retval == EOF || retval != 2)
749 break;
750
751 /* only add stats for disks, so we don't double count */
752 *n_reads += reads;
753 *n_writes += writes;
754 } else {
755 #if 0
756 /* individual partition stats */
757 retval = fscanf(f, " %*d %d %*d %d", &reads, &writes);
758 if (retval == EOF || retval != 2)
759 break;
760 #endif
761 /* assume the lines aren't longer that 1024 characters */
762 if (fgets(tmp, 1024, f) == NULL)
763 break;
764 }
765 }
766
767 fclose(f);
768 #else
769 *n_reads = 0;
770 *n_writes = 0;
771 #endif
772 }
773
774
775 #ifndef HAVE_STRLCPY
776 /* like strncpy but does not 0 fill the buffer and always null
777 terminates. bufsize is the size of the destination buffer */
strlcpy(char * d,const char * s,size_t bufsize)778 size_t strlcpy(char *d, const char *s, size_t bufsize)
779 {
780 size_t len = strlen(s);
781 size_t ret = len;
782 if (bufsize <= 0) return 0;
783 if (len >= bufsize) len = bufsize-1;
784 memcpy(d, s, len);
785 d[len] = 0;
786 return ret;
787 }
788 #endif
789
790 #ifndef HAVE_STRSEP
strsep(char ** str,const char * delims)791 static char* strsep(char** str, const char* delims)
792 {
793 char* token;
794
795 if (*str == NULL) {
796 return NULL;
797 }
798
799 token = *str;
800 while (**str != '\0') {
801 if (strchr(delims, **str) != NULL) {
802 **str = '\0';
803 (*str)++;
804 return token;
805 }
806 (*str)++;
807 }
808 *str = NULL;
809 return token;
810 }
811 #endif
812
813 /* Given a string @p input, this function fills a
814 a newly-allocated array of strings with copies of
815 the input's whitespace-separated parts.
816 Returns 0 on success, 1 on error.
817 */
dcc_tokenize_string(const char * input,char *** argv_ptr)818 int dcc_tokenize_string(const char *input, char ***argv_ptr)
819 {
820 size_t n_spaces = 0;
821 char *for_count;
822 char **ap;
823 char *input_copy;
824
825 /* First of all, make a copy of the input string;
826 * this way, we can destroy the copy.
827 */
828 input_copy = strdup(input);
829 if (input_copy == NULL)
830 return 1;
831
832 /* Count the spaces in the string. */
833 for (for_count = input_copy; *for_count; for_count++)
834 if (isspace((uint8_t)*for_count))
835 n_spaces++;
836
837 /* The maximum number of space-delimited strings we
838 * can have is n_spaces + 1, and we need to add another 1 for
839 * the null-termination.
840 */
841 *argv_ptr = malloc(sizeof(char*) * (n_spaces + 1 + 1));
842 if (*argv_ptr == NULL) {
843 free(input_copy);
844 return 1;
845 }
846
847 ap = *argv_ptr;
848 while((*ap = strsep(&input_copy, " \t\n")) != NULL) {
849
850 /* If the field is empty, do nothing */
851 if (**ap == '\0')
852 continue;
853
854 *ap = strdup(*ap);
855 if (*ap == NULL) {
856 char **p;
857 for (p = *argv_ptr; *p; p++) {
858 free(*p);
859 }
860 free(*argv_ptr);
861 free(input_copy);
862 return 1;
863 }
864
865 ap++;
866 }
867 free(input_copy);
868 return 0;
869 }
870
871 #ifndef HAVE_GETLINE
getline(char ** lineptr,size_t * n,FILE * stream)872 ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
873 static const int buffer_size_increment = 100;
874 char *buffer;
875 size_t size;
876 size_t bytes_read;
877 int c;
878 char *new_buffer;
879
880 if (lineptr == NULL || stream == NULL || n == NULL ||
881 (*lineptr == NULL && *n != 0)) {
882 /* Invalid parameters. */
883 return -1;
884 }
885
886 buffer = *lineptr;
887 size = *n;
888
889 bytes_read = 0;
890 do {
891 /* Ensure that we have space for next character or '\0'. */
892 if (bytes_read + 1 > size) {
893 size += buffer_size_increment;
894 new_buffer = realloc(buffer, size);
895 if (new_buffer == NULL) {
896 /* Out of memory. */
897 *lineptr = buffer;
898 *n = size - buffer_size_increment;
899 return -1;
900 }
901 buffer = new_buffer;
902 }
903 if ((c = fgetc(stream)) == EOF)
904 break;
905 buffer[bytes_read++] = c;
906 } while (c != '\n');
907 buffer[bytes_read] = '\0';
908
909 *lineptr = buffer;
910 *n = size;
911
912 /* We return -1 on EOF for compatibility with GNU getline(). */
913 return bytes_read == 0 ? -1 : (ssize_t) bytes_read;
914 }
915 #endif
916
917 /* from old systemd
918
919 Copyright 2010 Lennart Poettering
920
921 Permission is hereby granted, free of charge, to any person
922 obtaining a copy of this software and associated documentation files
923 (the "Software"), to deal in the Software without restriction,
924 including without limitation the rights to use, copy, modify, merge,
925 publish, distribute, sublicense, and/or sell copies of the Software,
926 and to permit persons to whom the Software is furnished to do so,
927 subject to the following conditions:
928
929 The above copyright notice and this permission notice shall be
930 included in all copies or substantial portions of the Software.
931
932 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
933 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
934 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
935 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
936 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
937 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
938 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
939 SOFTWARE.
940 */
sd_is_socket_internal(int fd,int type,int listening)941 static int sd_is_socket_internal(int fd, int type, int listening) {
942 struct stat st_fd;
943
944 if (fd < 0 || type < 0)
945 return -EINVAL;
946
947 if (fstat(fd, &st_fd) < 0)
948 return -errno;
949
950 if (!S_ISSOCK(st_fd.st_mode))
951 return 0;
952
953 if (type != 0) {
954 int other_type = 0;
955 socklen_t l = sizeof(other_type);
956
957 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
958 return -errno;
959
960 if (l != sizeof(other_type))
961 return -EINVAL;
962
963 if (other_type != type)
964 return 0;
965 }
966
967 if (listening >= 0) {
968 int accepting = 0;
969 socklen_t l = sizeof(accepting);
970
971 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
972 return -errno;
973
974 if (l != sizeof(accepting))
975 return -EINVAL;
976
977 if (!accepting != !listening)
978 return 0;
979 }
980
981 return 1;
982 }
983
984 union sockaddr_union {
985 struct sockaddr sa;
986 struct sockaddr_in in4;
987 struct sockaddr_in6 in6;
988 struct sockaddr_un un;
989 struct sockaddr_storage storage;
990 };
991
not_sd_is_socket(int fd,int family,int type,int listening)992 int not_sd_is_socket(int fd, int family, int type, int listening) {
993 int r;
994
995 if (family < 0)
996 return -EINVAL;
997
998 r = sd_is_socket_internal(fd, type, listening);
999 if (r <= 0)
1000 return r;
1001
1002 if (family > 0) {
1003 union sockaddr_union sockaddr = {};
1004 socklen_t l = sizeof(sockaddr);
1005
1006 if (getsockname(fd, &sockaddr.sa, &l) < 0)
1007 return -errno;
1008
1009 if ((size_t)l < sizeof(sa_family_t))
1010 return -EINVAL;
1011
1012 return sockaddr.sa.sa_family == family;
1013 }
1014
1015 return 1;
1016 }
1017