zmap-mark-ii/lib/util.c

372 lines
7.6 KiB
C

/*
* Copyright 2021 Regents of the University of Michigan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _GNU_SOURCE
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "includes.h"
#include "xalloc.h"
#include <errno.h>
#include <unistd.h>
#include <sched.h>
#include <pthread.h>
#include <sys/types.h>
#include <pwd.h>
#include "../lib/logger.h"
#define MAX_SPLITS 128
int max_int(int a, int b)
{
if (a >= b) {
return a;
}
return b;
}
int min_int(int a, int b)
{
if (a >= b) {
return b;
}
return a;
}
void enforce_range(const char *name, int v, int min, int max)
{
if (check_range(v, min, max) == EXIT_FAILURE) {
log_fatal("zmap", "argument `%s' must be between %d and %d\n",
name, min, max);
}
}
void split_string(const char *in, int *len, const char ***results)
{
const char **fields = xcalloc(MAX_SPLITS, sizeof(const char *));
int retvlen = 0;
const char *currloc = in;
// parse csv into a set of strings
while (1) {
assert(retvlen < MAX_SPLITS);
size_t len = strcspn(currloc, ", ");
if (len == 0) {
currloc++;
} else {
char *new = xmalloc(len + 1);
strncpy(new, currloc, len);
new[len] = '\0';
fields[retvlen++] = new;
assert(fields[retvlen - 1]);
}
if (len == strlen(currloc)) {
break;
}
currloc += len;
}
*results = fields;
*len = retvlen;
}
void fprintw(FILE *f, const char *s, size_t w)
{
if (strlen(s) <= w) {
fprintf(f, "%s", s);
return;
}
// process each line individually in order to
// respect existing line breaks in string.
char *news = strdup(s);
char *pch = strtok(news, "\n");
while (pch) {
if (strlen(pch) <= w) {
printf("%s\n", pch);
pch = strtok(NULL, "\n");
continue;
}
char *t = pch;
while (strlen(t)) {
size_t numchars = 0; // number of chars to print
char *tmp = t;
while (1) {
size_t new = strcspn(tmp, " ") + 1;
if (new == strlen(tmp) || new > w) {
// there are no spaces in the string,
// so, just print the entire thing on
// one line;
numchars += new;
break;
} else if (numchars + new > w) {
// if we added any more, we'd be over w
// chars so time to print the line and
// move on to the next.
break;
} else {
tmp += (size_t) new;
numchars += new;
}
}
fprintf(f, "%.*s\n", (int)numchars, t);
t += (size_t)numchars;
if (t > pch + (size_t)strlen(pch)) {
break;
}
}
pch = strtok(NULL, "\n");
}
free(news);
}
uint32_t parse_max_hosts(char *max_targets)
{
char *end;
errno = 0;
double v = strtod(max_targets, &end);
if (end == max_targets || errno != 0) {
log_fatal("argparse", "can't convert max-targets to a number");
}
if (end[0] == '%' && end[1] == '\0') {
// treat as percentage
v = v * ((unsigned long long int)1 << 32) / 100.;
} else if (end[0] != '\0') {
log_fatal("eargparse", "extra characters after max-targets");
}
if (v <= 0) {
return 0;
} else if (v >= ((unsigned long long int)1 << 32)) {
return 0xFFFFFFFF;
} else {
return v;
}
}
// pretty print elapsed (or estimated) number of seconds
void time_string(uint32_t time, int est, char *buf, size_t len)
{
int y = time / 31556736;
int d = (time % 31556736) / 86400;
int h = (time % 86400) / 3600;
int m = (time % 3600) / 60;
int s = time % 60;
if (est) {
if (y > 0) {
snprintf(buf, len, "%d years", y);
} else if (d > 9) {
snprintf(buf, len, "%dd", d);
} else if (d > 0) {
snprintf(buf, len, "%dd%02dh", d, h);
} else if (h > 9) {
snprintf(buf, len, "%dh", h);
} else if (h > 0) {
snprintf(buf, len, "%dh%02dm", h, m);
} else if (m > 9) {
snprintf(buf, len, "%dm", m);
} else if (m > 0) {
snprintf(buf, len, "%dm%02ds", m, s);
} else {
snprintf(buf, len, "%ds", s);
}
} else {
if (d > 0) {
snprintf(buf, len, "%dd%d:%02d:%02d", d, h, m, s);
} else if (h > 0) {
snprintf(buf, len, "%d:%02d:%02d", h, m, s);
} else {
snprintf(buf, len, "%d:%02d", m, s);
}
}
}
// pretty print quantities
void number_string(uint32_t n, char *buf, size_t len)
{
int figs = 0;
if (n < 1000) {
snprintf(buf, len, "%u ", n);
} else if (n < 1000000) {
if (n < 10000) {
figs = 2;
} else if (n < 100000) {
figs = 1;
}
snprintf(buf, len, "%0.*f K", figs, (float)n / 1000.);
} else {
if (figs < 10000000) {
figs = 2;
} else if (figs < 100000000) {
figs = 1;
}
snprintf(buf, len, "%0.*f M", figs, (float)n / 1000000.);
}
}
int parse_mac(macaddr_t *out, char *in)
{
if (strlen(in) < MAC_ADDR_LEN * 3 - 1)
return 0;
char octet[4];
octet[2] = '\0';
for (int i = 0; i < MAC_ADDR_LEN; i++) {
if (i < MAC_ADDR_LEN - 1 && in[i * 3 + 2] != ':') {
return 0;
}
strncpy(octet, &in[i * 3], 2);
char *err = NULL;
long b = strtol(octet, &err, 16);
if (err && *err != '\0') {
return 0;
}
out[i] = b & 0xFF;
}
return 1;
}
int check_range(int v, int min, int max)
{
if (v < min || v > max) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int file_exists(char *name)
{
FILE *file = fopen(name, "r");
if (!file)
return 0;
fclose(file);
return 1;
}
#if defined(__APPLE__)
#include <uuid/uuid.h>
#endif
int drop_privs(void)
{
struct passwd *pw;
if (geteuid() != 0) {
/* Not root */
return EXIT_SUCCESS;
}
if ((pw = getpwnam("nobody")) != NULL) {
if (setuid(pw->pw_uid) == 0) {
return EXIT_SUCCESS; // success
}
}
return EXIT_FAILURE;
}
#if defined(__APPLE__)
#include <mach/thread_act.h>
int set_cpu(uint32_t core)
{
mach_port_t tid = pthread_mach_thread_np(pthread_self());
struct thread_affinity_policy policy;
policy.affinity_tag = core;
kern_return_t ret = thread_policy_set(tid, THREAD_AFFINITY_POLICY,
(thread_policy_t)&policy,
THREAD_AFFINITY_POLICY_COUNT);
if (ret != KERN_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#elif defined(__DragonFly__)
#include <sys/usched.h>
int set_cpu(uint32_t core)
{
if (usched_set(getpid(), USCHED_SET_CPU, &core, sizeof(core)) != 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#elif defined(__NetBSD__)
int set_cpu(uint32_t core)
{
cpuset_t *cpuset = cpuset_create();
if (cpuset == NULL) {
return EXIT_FAILURE;
}
cpuset_zero(cpuset);
cpuset_set(core, cpuset);
cpuset_destroy(cpuset);
if (pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset),
cpuset) != 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#else
#if defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/cpuset.h>
#include <pthread_np.h>
#define cpu_set_t cpuset_t
#endif
int set_cpu(uint32_t core)
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core, &cpuset);
if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
&cpuset) != 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#endif
double now(void)
{
struct timeval now;
gettimeofday(&now, NULL);
return (double)now.tv_sec + (double)now.tv_usec / 1000000.;
}
double steady_now(void)
{
#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp);
return (double)tp.tv_sec + (double)tp.tv_nsec / 1000000000.;
#else
struct timeval now;
gettimeofday(&now, NULL);
return (double)now.tv_sec + (double)now.tv_usec / 1000000.;
#endif
}