2011-04-20 20:33:09 +00:00
|
|
|
/*
|
2017-04-20 12:26:39 -07:00
|
|
|
* iperf, Copyright (c) 2014, 2016, 2017, The Regents of the University of
|
2014-09-29 14:00:46 -07:00
|
|
|
* California, through Lawrence Berkeley National Laboratory (subject
|
|
|
|
* to receipt of any required approvals from the U.S. Dept. of
|
|
|
|
* Energy). All rights reserved.
|
2011-04-20 20:33:09 +00:00
|
|
|
*
|
2014-09-29 14:00:46 -07:00
|
|
|
* If you have questions about your rights to use or distribute this
|
|
|
|
* software, please contact Berkeley Lab's Technology Transfer
|
|
|
|
* Department at TTD@lbl.gov.
|
|
|
|
*
|
|
|
|
* NOTICE. This software is owned by the U.S. Department of Energy.
|
|
|
|
* As such, the U.S. Government has been granted for itself and others
|
|
|
|
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
|
|
|
* worldwide license in the Software to reproduce, prepare derivative
|
|
|
|
* works, and perform publicly and display publicly. Beginning five
|
|
|
|
* (5) years after the date permission to assert copyright is obtained
|
|
|
|
* from the U.S. Department of Energy, and subject to any subsequent
|
|
|
|
* five (5) year renewals, the U.S. Government is granted for itself
|
|
|
|
* and others acting on its behalf a paid-up, nonexclusive,
|
|
|
|
* irrevocable, worldwide license in the Software to reproduce,
|
|
|
|
* prepare derivative works, distribute copies to the public, perform
|
|
|
|
* publicly and display publicly, and to permit others to do so.
|
|
|
|
*
|
|
|
|
* This code is distributed under a BSD style license, see the LICENSE
|
|
|
|
* file for complete information.
|
2011-04-20 20:33:09 +00:00
|
|
|
*/
|
2010-07-22 18:57:08 +00:00
|
|
|
/* iperf_util.c
|
|
|
|
*
|
|
|
|
* Iperf utility functions
|
|
|
|
*
|
|
|
|
*/
|
2014-04-14 13:33:33 -07:00
|
|
|
#include "iperf_config.h"
|
2009-12-10 14:20:48 +00:00
|
|
|
|
2009-07-23 18:22:24 +00:00
|
|
|
#include <stdio.h>
|
2018-01-03 12:00:27 -08:00
|
|
|
#include <signal.h>
|
2009-07-23 18:22:24 +00:00
|
|
|
#include <stdlib.h>
|
2012-09-28 16:00:14 -07:00
|
|
|
#include <unistd.h>
|
2010-07-22 18:57:08 +00:00
|
|
|
#include <string.h>
|
2013-02-07 12:35:17 -08:00
|
|
|
#include <stdarg.h>
|
2010-07-22 18:57:08 +00:00
|
|
|
#include <sys/select.h>
|
2012-09-28 16:00:14 -07:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
2013-11-26 14:47:15 -08:00
|
|
|
#include <sys/resource.h>
|
2014-01-30 15:15:53 -08:00
|
|
|
#include <sys/utsname.h>
|
2012-09-28 16:00:14 -07:00
|
|
|
#include <time.h>
|
2010-07-22 18:57:08 +00:00
|
|
|
#include <errno.h>
|
2017-11-11 10:12:55 -08:00
|
|
|
#include <fcntl.h>
|
2010-07-22 18:57:08 +00:00
|
|
|
|
2013-02-07 12:35:17 -08:00
|
|
|
#include "cjson.h"
|
2017-05-21 21:30:18 +03:00
|
|
|
#include "iperf.h"
|
|
|
|
#include "iperf_api.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read entropy from /dev/urandom
|
|
|
|
* Errors are fatal.
|
|
|
|
* Returns 0 on success.
|
|
|
|
*/
|
|
|
|
int readentropy(void *out, size_t outsize)
|
|
|
|
{
|
|
|
|
static FILE *frandom;
|
|
|
|
static const char rndfile[] = "/dev/urandom";
|
|
|
|
|
|
|
|
if (!outsize) return 0;
|
|
|
|
|
|
|
|
if (frandom == NULL) {
|
|
|
|
frandom = fopen(rndfile, "rb");
|
|
|
|
if (frandom == NULL) {
|
|
|
|
iperf_errexit(NULL, "error - failed to open %s: %s\n",
|
|
|
|
rndfile, strerror(errno));
|
|
|
|
}
|
|
|
|
setbuf(frandom, NULL);
|
|
|
|
}
|
|
|
|
if (fread(out, 1, outsize, frandom) != outsize) {
|
|
|
|
iperf_errexit(NULL, "error - failed to read %s: %s\n",
|
|
|
|
rndfile,
|
|
|
|
feof(frandom) ? "EOF" : strerror(errno));
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
|
2018-04-20 17:25:24 +03:00
|
|
|
/*
|
|
|
|
* Fills buffer with repeating pattern (similar to pattern that used in iperf2)
|
|
|
|
*/
|
|
|
|
void fill_with_repeating_pattern(void *out, size_t outsize)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
int counter = 0;
|
|
|
|
char *buf = (char *)out;
|
|
|
|
|
|
|
|
if (!outsize) return;
|
|
|
|
|
|
|
|
for (i = 0; i < outsize; i++) {
|
|
|
|
buf[i] = (char)('0' + counter);
|
|
|
|
if (counter >= 9)
|
|
|
|
counter = 0;
|
|
|
|
else
|
|
|
|
counter++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-10 12:43:50 -07:00
|
|
|
/* make_cookie
|
2010-07-22 18:57:08 +00:00
|
|
|
*
|
2012-08-10 12:43:50 -07:00
|
|
|
* Generate and return a cookie string
|
2010-07-22 18:57:08 +00:00
|
|
|
*
|
|
|
|
* Iperf uses this function to create test "cookies" which
|
|
|
|
* server as unique test identifiers. These cookies are also
|
|
|
|
* used for the authentication of stream connections.
|
2017-05-21 21:30:18 +03:00
|
|
|
* Assumes cookie has size (COOKIE_SIZE + 1) char's.
|
2010-07-22 18:57:08 +00:00
|
|
|
*/
|
2009-10-24 20:43:06 +00:00
|
|
|
|
2009-07-24 18:21:50 +00:00
|
|
|
void
|
2012-08-10 12:43:50 -07:00
|
|
|
make_cookie(char *cookie)
|
2009-07-23 18:22:24 +00:00
|
|
|
{
|
2017-05-21 21:30:18 +03:00
|
|
|
unsigned char *out = (unsigned char*)cookie;
|
|
|
|
size_t pos;
|
|
|
|
static const unsigned char rndchars[] = "abcdefghijklmnopqrstuvwxyz234567";
|
2010-06-30 15:58:16 +00:00
|
|
|
|
2017-05-21 21:30:18 +03:00
|
|
|
readentropy(out, COOKIE_SIZE);
|
|
|
|
for (pos = 0; pos < (COOKIE_SIZE - 1); pos++) {
|
|
|
|
out[pos] = rndchars[out[pos] % (sizeof(rndchars) - 1)];
|
|
|
|
}
|
|
|
|
out[pos] = '\0';
|
2009-07-23 18:22:24 +00:00
|
|
|
}
|
2010-07-22 18:57:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* is_closed
|
|
|
|
*
|
|
|
|
* Test if the file descriptor fd is closed.
|
|
|
|
*
|
|
|
|
* Iperf uses this function to test whether a TCP stream socket
|
|
|
|
* is closed, because accepting and denying an invalid connection
|
|
|
|
* in iperf_tcp_accept is not considered an error.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
is_closed(int fd)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
fd_set readset;
|
|
|
|
|
|
|
|
FD_ZERO(&readset);
|
|
|
|
FD_SET(fd, &readset);
|
|
|
|
tv.tv_sec = 0;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
|
|
|
|
if (select(fd+1, &readset, NULL, NULL, &tv) < 0) {
|
|
|
|
if (errno == EBADF)
|
2012-12-11 22:29:26 -08:00
|
|
|
return 1;
|
2010-07-22 18:57:08 +00:00
|
|
|
}
|
2012-12-11 22:29:26 -08:00
|
|
|
return 0;
|
2010-07-22 18:57:08 +00:00
|
|
|
}
|
2012-12-05 12:34:58 -08:00
|
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
timeval_to_double(struct timeval * tv)
|
|
|
|
{
|
|
|
|
double d;
|
|
|
|
|
|
|
|
d = tv->tv_sec + tv->tv_usec / 1000000;
|
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
timeval_equals(struct timeval * tv0, struct timeval * tv1)
|
|
|
|
{
|
|
|
|
if ( tv0->tv_sec == tv1->tv_sec && tv0->tv_usec == tv1->tv_usec )
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
double
|
|
|
|
timeval_diff(struct timeval * tv0, struct timeval * tv1)
|
|
|
|
{
|
|
|
|
double time1, time2;
|
|
|
|
|
|
|
|
time1 = tv0->tv_sec + (tv0->tv_usec / 1000000.0);
|
|
|
|
time2 = tv1->tv_sec + (tv1->tv_usec / 1000000.0);
|
|
|
|
|
|
|
|
time1 = time1 - time2;
|
|
|
|
if (time1 < 0)
|
|
|
|
time1 = -time1;
|
2012-12-11 22:29:26 -08:00
|
|
|
return time1;
|
2012-12-05 12:34:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-11-26 14:47:15 -08:00
|
|
|
cpu_util(double pcpu[3])
|
2012-12-05 12:34:58 -08:00
|
|
|
{
|
2018-05-16 23:49:45 +02:00
|
|
|
static struct iperf_time last;
|
2012-12-05 12:34:58 -08:00
|
|
|
static clock_t clast;
|
2013-11-26 14:47:15 -08:00
|
|
|
static struct rusage rlast;
|
2018-05-16 23:49:45 +02:00
|
|
|
struct iperf_time now, temp_time;
|
2012-12-05 12:34:58 -08:00
|
|
|
clock_t ctemp;
|
2013-11-26 14:47:15 -08:00
|
|
|
struct rusage rtemp;
|
2012-12-05 12:34:58 -08:00
|
|
|
double timediff;
|
2013-11-26 14:47:15 -08:00
|
|
|
double userdiff;
|
|
|
|
double systemdiff;
|
2012-12-05 12:34:58 -08:00
|
|
|
|
|
|
|
if (pcpu == NULL) {
|
2018-05-16 23:49:45 +02:00
|
|
|
iperf_time_now(&last);
|
2012-12-05 12:34:58 -08:00
|
|
|
clast = clock();
|
2013-11-26 14:47:15 -08:00
|
|
|
getrusage(RUSAGE_SELF, &rlast);
|
2012-12-05 12:34:58 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-16 23:49:45 +02:00
|
|
|
iperf_time_now(&now);
|
2012-12-05 12:34:58 -08:00
|
|
|
ctemp = clock();
|
2013-11-26 14:47:15 -08:00
|
|
|
getrusage(RUSAGE_SELF, &rtemp);
|
2012-12-05 12:34:58 -08:00
|
|
|
|
2018-05-16 23:49:45 +02:00
|
|
|
iperf_time_diff(&now, &last, &temp_time);
|
2018-12-13 23:43:02 +02:00
|
|
|
timediff = iperf_time_in_usecs(&temp_time);
|
2018-05-16 23:49:45 +02:00
|
|
|
|
2013-11-26 14:47:15 -08:00
|
|
|
userdiff = ((rtemp.ru_utime.tv_sec * 1000000.0 + rtemp.ru_utime.tv_usec) -
|
|
|
|
(rlast.ru_utime.tv_sec * 1000000.0 + rlast.ru_utime.tv_usec));
|
|
|
|
systemdiff = ((rtemp.ru_stime.tv_sec * 1000000.0 + rtemp.ru_stime.tv_usec) -
|
|
|
|
(rlast.ru_stime.tv_sec * 1000000.0 + rlast.ru_stime.tv_usec));
|
|
|
|
|
2014-04-25 12:50:15 -07:00
|
|
|
pcpu[0] = (((ctemp - clast) * 1000000.0 / CLOCKS_PER_SEC) / timediff) * 100;
|
2013-11-26 14:47:15 -08:00
|
|
|
pcpu[1] = (userdiff / timediff) * 100;
|
|
|
|
pcpu[2] = (systemdiff / timediff) * 100;
|
2012-12-05 12:34:58 -08:00
|
|
|
}
|
2013-02-07 12:35:17 -08:00
|
|
|
|
2014-04-14 13:33:33 -07:00
|
|
|
const char *
|
2013-03-08 20:56:52 -08:00
|
|
|
get_system_info(void)
|
2014-01-30 15:15:53 -08:00
|
|
|
{
|
|
|
|
static char buf[1024];
|
|
|
|
struct utsname uts;
|
|
|
|
|
|
|
|
memset(buf, 0, 1024);
|
|
|
|
uname(&uts);
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%s %s %s %s %s", uts.sysname, uts.nodename,
|
|
|
|
uts.release, uts.version, uts.machine);
|
|
|
|
|
2013-03-08 20:56:52 -08:00
|
|
|
return buf;
|
2014-01-30 15:15:53 -08:00
|
|
|
}
|
2013-03-08 20:56:52 -08:00
|
|
|
|
|
|
|
|
2014-04-14 13:33:33 -07:00
|
|
|
const char *
|
|
|
|
get_optional_features(void)
|
|
|
|
{
|
|
|
|
static char features[1024];
|
|
|
|
unsigned int numfeatures = 0;
|
|
|
|
|
|
|
|
snprintf(features, sizeof(features), "Optional features available: ");
|
|
|
|
|
|
|
|
#if defined(HAVE_CPU_AFFINITY)
|
|
|
|
if (numfeatures > 0) {
|
|
|
|
strncat(features, ", ",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
}
|
|
|
|
strncat(features, "CPU affinity setting",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
numfeatures++;
|
|
|
|
#endif /* HAVE_CPU_AFFINITY */
|
|
|
|
|
|
|
|
#if defined(HAVE_FLOWLABEL)
|
|
|
|
if (numfeatures > 0) {
|
|
|
|
strncat(features, ", ",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
}
|
|
|
|
strncat(features, "IPv6 flow label",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
numfeatures++;
|
|
|
|
#endif /* HAVE_FLOWLABEL */
|
|
|
|
|
|
|
|
#if defined(HAVE_SCTP)
|
|
|
|
if (numfeatures > 0) {
|
|
|
|
strncat(features, ", ",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
}
|
|
|
|
strncat(features, "SCTP",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
numfeatures++;
|
|
|
|
#endif /* HAVE_SCTP */
|
|
|
|
|
|
|
|
#if defined(HAVE_TCP_CONGESTION)
|
|
|
|
if (numfeatures > 0) {
|
|
|
|
strncat(features, ", ",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
}
|
|
|
|
strncat(features, "TCP congestion algorithm setting",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
numfeatures++;
|
|
|
|
#endif /* HAVE_TCP_CONGESTION */
|
|
|
|
|
2014-04-14 14:16:07 -07:00
|
|
|
#if defined(HAVE_SENDFILE)
|
|
|
|
if (numfeatures > 0) {
|
|
|
|
strncat(features, ", ",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
}
|
|
|
|
strncat(features, "sendfile / zerocopy",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
numfeatures++;
|
|
|
|
#endif /* HAVE_SENDFILE */
|
|
|
|
|
2016-05-26 09:47:48 -07:00
|
|
|
#if defined(HAVE_SO_MAX_PACING_RATE)
|
|
|
|
if (numfeatures > 0) {
|
|
|
|
strncat(features, ", ",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
}
|
|
|
|
strncat(features, "socket pacing",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
numfeatures++;
|
|
|
|
#endif /* HAVE_SO_MAX_PACING_RATE */
|
|
|
|
|
2017-04-20 12:26:39 -07:00
|
|
|
#if defined(HAVE_SSL)
|
|
|
|
if (numfeatures > 0) {
|
|
|
|
strncat(features, ", ",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
}
|
|
|
|
strncat(features, "authentication",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
numfeatures++;
|
|
|
|
#endif /* HAVE_SSL */
|
|
|
|
|
2014-04-14 13:33:33 -07:00
|
|
|
if (numfeatures == 0) {
|
|
|
|
strncat(features, "None",
|
|
|
|
sizeof(features) - strlen(features) - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return features;
|
|
|
|
}
|
|
|
|
|
2013-02-07 12:35:17 -08:00
|
|
|
/* Helper routine for building cJSON objects in a printf-like manner.
|
|
|
|
**
|
|
|
|
** Sample call:
|
|
|
|
** j = iperf_json_printf("foo: %b bar: %d bletch: %f eep: %s", b, i, f, s);
|
|
|
|
**
|
|
|
|
** The four formatting characters and the types they expect are:
|
|
|
|
** %b boolean int
|
|
|
|
** %d integer int64_t
|
|
|
|
** %f floating point double
|
|
|
|
** %s string char *
|
|
|
|
** If the values you're passing in are not these exact types, you must
|
|
|
|
** cast them, there is no automatic type coercion/widening here.
|
|
|
|
**
|
|
|
|
** The colons mark the end of field names, and blanks are ignored.
|
|
|
|
**
|
|
|
|
** This routine is not particularly robust, but it's not part of the API,
|
|
|
|
** it's just for internal iperf3 use.
|
|
|
|
*/
|
|
|
|
cJSON*
|
|
|
|
iperf_json_printf(const char *format, ...)
|
|
|
|
{
|
|
|
|
cJSON* o;
|
|
|
|
va_list argp;
|
|
|
|
const char *cp;
|
|
|
|
char name[100];
|
|
|
|
char* np;
|
|
|
|
cJSON* j;
|
|
|
|
|
|
|
|
o = cJSON_CreateObject();
|
|
|
|
if (o == NULL)
|
|
|
|
return NULL;
|
|
|
|
va_start(argp, format);
|
|
|
|
np = name;
|
|
|
|
for (cp = format; *cp != '\0'; ++cp) {
|
|
|
|
switch (*cp) {
|
|
|
|
case ' ':
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
*np = '\0';
|
|
|
|
break;
|
|
|
|
case '%':
|
|
|
|
++cp;
|
|
|
|
switch (*cp) {
|
|
|
|
case 'b':
|
|
|
|
j = cJSON_CreateBool(va_arg(argp, int));
|
|
|
|
break;
|
|
|
|
case 'd':
|
2016-06-03 09:23:59 -07:00
|
|
|
j = cJSON_CreateNumber(va_arg(argp, int64_t));
|
2013-02-07 12:35:17 -08:00
|
|
|
break;
|
|
|
|
case 'f':
|
2016-06-03 09:23:59 -07:00
|
|
|
j = cJSON_CreateNumber(va_arg(argp, double));
|
2013-02-07 12:35:17 -08:00
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
j = cJSON_CreateString(va_arg(argp, char *));
|
|
|
|
break;
|
|
|
|
default:
|
2016-05-26 21:36:16 +05:30
|
|
|
va_end(argp);
|
2013-02-07 12:35:17 -08:00
|
|
|
return NULL;
|
|
|
|
}
|
2016-05-26 21:36:16 +05:30
|
|
|
if (j == NULL) {
|
|
|
|
va_end(argp);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-02-07 12:35:17 -08:00
|
|
|
cJSON_AddItemToObject(o, name, j);
|
|
|
|
np = name;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*np++ = *cp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
va_end(argp);
|
|
|
|
return o;
|
|
|
|
}
|
2013-12-05 08:49:27 -08:00
|
|
|
|
|
|
|
/* Debugging routine to dump out an fd_set. */
|
|
|
|
void
|
|
|
|
iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int comma;
|
|
|
|
|
|
|
|
fprintf(fp, "%s: [", str);
|
|
|
|
comma = 0;
|
|
|
|
for (fd = 0; fd < nfds; ++fd) {
|
|
|
|
if (FD_ISSET(fd, fds)) {
|
|
|
|
if (comma)
|
|
|
|
fprintf(fp, ", ");
|
|
|
|
fprintf(fp, "%d", fd);
|
|
|
|
comma = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(fp, "]\n");
|
|
|
|
}
|
2017-11-08 10:18:30 -08:00
|
|
|
|
2017-11-11 10:12:55 -08:00
|
|
|
/*
|
|
|
|
* daemon(3) implementation for systems lacking one.
|
|
|
|
* Cobbled together from various daemon(3) implementations,
|
|
|
|
* not intended to be general-purpose. */
|
|
|
|
#ifndef HAVE_DAEMON
|
|
|
|
int daemon(int nochdir, int noclose)
|
|
|
|
{
|
|
|
|
pid_t pid = 0;
|
|
|
|
pid_t sid = 0;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore any possible SIGHUP when the parent process exits.
|
|
|
|
* Note that the iperf3 server process will eventually install
|
|
|
|
* its own signal handler for SIGHUP, so we can be a little
|
|
|
|
* sloppy about not restoring the prior value. This does not
|
|
|
|
* generalize.
|
|
|
|
*/
|
|
|
|
signal(SIGHUP, SIG_IGN);
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
if (pid < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (pid > 0) {
|
|
|
|
/* Use _exit() to avoid doing atexit() stuff. */
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
sid = setsid();
|
|
|
|
if (sid < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fork again to avoid becoming a session leader.
|
|
|
|
* This might only matter on old SVr4-derived OSs.
|
|
|
|
* Note in particular that glibc and FreeBSD libc
|
|
|
|
* only fork once.
|
|
|
|
*/
|
|
|
|
pid = fork();
|
|
|
|
if (pid == -1) {
|
|
|
|
return -1;
|
|
|
|
} else if (pid != 0) {
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nochdir) {
|
|
|
|
chdir("/");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
|
|
|
dup2(fd, STDIN_FILENO);
|
|
|
|
dup2(fd, STDOUT_FILENO);
|
|
|
|
dup2(fd, STDERR_FILENO);
|
|
|
|
if (fd > 2) {
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_DAEMON */
|
|
|
|
|
2017-11-08 10:18:30 -08:00
|
|
|
/* Compatibility version of getline(3) for systems that don't have it.. */
|
|
|
|
#ifndef HAVE_GETLINE
|
|
|
|
/* The following code adopted from NetBSD's getline.c, which is: */
|
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2011 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Christos Zoulas.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
ssize_t
|
|
|
|
getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
|
|
|
|
{
|
|
|
|
char *ptr, *eptr;
|
|
|
|
|
|
|
|
|
|
|
|
if (*buf == NULL || *bufsiz == 0) {
|
|
|
|
*bufsiz = BUFSIZ;
|
|
|
|
if ((*buf = malloc(*bufsiz)) == NULL)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ptr = *buf, eptr = *buf + *bufsiz;;) {
|
|
|
|
int c = fgetc(fp);
|
|
|
|
if (c == -1) {
|
|
|
|
if (feof(fp)) {
|
|
|
|
ssize_t diff = (ssize_t)(ptr - *buf);
|
|
|
|
if (diff != 0) {
|
|
|
|
*ptr = '\0';
|
|
|
|
return diff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*ptr++ = c;
|
|
|
|
if (c == delimiter) {
|
|
|
|
*ptr = '\0';
|
|
|
|
return ptr - *buf;
|
|
|
|
}
|
|
|
|
if (ptr + 2 >= eptr) {
|
|
|
|
char *nbuf;
|
|
|
|
size_t nbufsiz = *bufsiz * 2;
|
|
|
|
ssize_t d = ptr - *buf;
|
|
|
|
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
|
|
|
|
return -1;
|
|
|
|
*buf = nbuf;
|
|
|
|
*bufsiz = nbufsiz;
|
|
|
|
eptr = nbuf + nbufsiz;
|
|
|
|
ptr = nbuf + d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
getline(char **buf, size_t *bufsiz, FILE *fp)
|
|
|
|
{
|
|
|
|
return getdelim(buf, bufsiz, '\n', fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|