From: Gunnar Beutner Date: Sun, 24 Jun 2012 16:44:07 +0000 (+0200) Subject: Bugfixes. X-Git-Tag: v0.0.1~362 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3d6df6611c04f25bcb15e0eb6a5dcedda6d5e629;p=icinga2 Bugfixes. --- diff --git a/base/tlsclient.cpp b/base/tlsclient.cpp index 3ac84a96e..4c63a9f7e 100644 --- a/base/tlsclient.cpp +++ b/base/tlsclient.cpp @@ -246,8 +246,6 @@ int TlsClient::SSLVerifyCertificate(int ok, X509_STORE_CTX *x509Context) SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(x509Context, SSL_get_ex_data_X509_STORE_CTX_idx()); TlsClient *client = (TlsClient *)SSL_get_ex_data(ssl, m_SSLIndex); - assert(client->GetMutex().active_count); - if (client == NULL) return 0; diff --git a/icinga/nagioschecktask.cpp b/icinga/nagioschecktask.cpp index 800dca309..64f20c177 100644 --- a/icinga/nagioschecktask.cpp +++ b/icinga/nagioschecktask.cpp @@ -44,10 +44,10 @@ void NagiosCheckTask::CheckThreadProc(void) mutex::scoped_lock lock(m_Mutex); map tasks; - const int maxTasks = 16; + const int maxTasks = 128; for (;;) { - while (m_Tasks.empty() || tasks.size() >= maxTasks) { + while (m_Tasks.empty() || tasks.size() >= MaxChecksPerThread) { lock.unlock(); map::iterator it, prev; @@ -202,7 +202,10 @@ void NagiosCheckTask::Register(void) { CheckTask::RegisterType("nagios", NagiosCheckTask::CreateTask, NagiosCheckTask::FlushQueue); - int numThreads = max(4, boost::thread::hardware_concurrency()); + int numThreads = boost::thread::hardware_concurrency(); + + if (numThreads < 4) + numThreads = 4; for (int i = 0; i < numThreads; i++) { thread t(&NagiosCheckTask::CheckThreadProc); diff --git a/icinga/nagioschecktask.h b/icinga/nagioschecktask.h index 16141857d..55643eb16 100644 --- a/icinga/nagioschecktask.h +++ b/icinga/nagioschecktask.h @@ -10,6 +10,8 @@ public: typedef shared_ptr Ptr; typedef weak_ptr WeakPtr; + static const int MaxChecksPerThread = 128; + NagiosCheckTask(const Service& service); virtual void Enqueue(void); diff --git a/third-party/popen-noshell/README b/third-party/popen-noshell/README index 586f8781d..7a7d4753f 100644 --- a/third-party/popen-noshell/README +++ b/third-party/popen-noshell/README @@ -1,6 +1,7 @@ /* * popen_noshell: A faster implementation of popen() and system() for Linux. * Copyright (c) 2009 Ivan Zahariev (famzah) + * Copyright (c) 2012 Gunnar Beutner * Version: 1.0 * * This program is free software; you can redistribute it and/or modify @@ -22,9 +23,5 @@ The package provides the following files: * The C implementation of the library: popen_noshell.c popen_noshell.h - * Examples: - popen_noshell_examples.c - * Unit tests: - popen_noshell_tests.c Compile instructions are included in each file. diff --git a/third-party/popen-noshell/performance_tests/fork-performance b/third-party/popen-noshell/performance_tests/fork-performance deleted file mode 100755 index 7a3d8ba4c..000000000 Binary files a/third-party/popen-noshell/performance_tests/fork-performance and /dev/null differ diff --git a/third-party/popen-noshell/performance_tests/fork-performance.c b/third-party/popen-noshell/performance_tests/fork-performance.c deleted file mode 100644 index f9d577b4e..000000000 --- a/third-party/popen-noshell/performance_tests/fork-performance.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * popen_noshell: A faster implementation of popen() and system() for Linux. - * Copyright (c) 2009 Ivan Zahariev (famzah) - * Version: 1.0 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; under version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "popen_noshell.h" - -/* - * This is a performance test program. - * It invokes the extremely light, statically build binary "./tiny2" which outputs "Hello, world!" and exits. - * - * Different approaches for calling "./tiny2" are tried, in order to compare their performance results. - * - */ - -#define USE_LIBC_POPEN 0 -#define USE_NOSHELL_POPEN 1 - -int use_noshell_compat = 0; - -void popen_test(int type) { - char *exec_file = "./tiny2"; - char *arg1 = (char *) NULL; - char *argv[] = {exec_file, arg1}; - FILE *fp; - struct popen_noshell_pass_to_pclose pclose_arg; - int status; - char buf[64]; - - if (type) { - if (!use_noshell_compat) { - fp = popen_noshell(exec_file, (const char * const *)argv, "r", &pclose_arg, 0); - } else { - fp = popen_noshell_compat(exec_file, "r", &pclose_arg); - argv[0] = NULL; // satisfy GCC warnings - } - } else { - fp = popen("./tiny2", "r"); - } - if (!fp) { - err(EXIT_FAILURE, "popen()"); - } - - while (fgets(buf, sizeof(buf)-1, fp)) { - if (strcmp(buf, "Hello, world!\n") != 0) { - errx(EXIT_FAILURE, "bad response: %s", buf); - } - } - - if (type) { - status = pclose_noshell(&pclose_arg); - } else { - status = pclose(fp); - } - if (status == -1) { - err(EXIT_FAILURE, "pclose()"); - } - if (status != 0) { - errx(EXIT_FAILURE, "status code is non-zero"); - } -} - -void fork_test(int type) { - pid_t pid; - int status; - - if (type) { - pid = fork(); - } else { - pid = vfork(); - } - if (pid == -1) { - err(EXIT_FAILURE, "fork()"); - } - - if (pid == 0) { // child - execl("./tiny2", "./tiny2", (char *) NULL); - _exit(255); - } - - // parent process - if (waitpid(pid, &status, 0) != pid) { - err(EXIT_FAILURE, "waitpid()"); - } - if (status != 0) { - errx(EXIT_FAILURE, "status code is non-zero"); - } -} - -char *allocate_memory(int size_in_mb, int ratio) { - char *m; - int size; - int i; - - size = size_in_mb*1024*1024; - m = malloc(sizeof(char) * size); - if (!m) { - err(EXIT_FAILURE, "malloc()"); - } - - /* allocate part of the memory, so that we can simulate some memory activity before fork()'ing */ - if (ratio != 0) { - for (i = 0; i < size/ratio; ++i) { - *(m + i) = 'z'; - } - } - - return m; -} - -int safe_atoi(char *s) { - int i; - - if (strlen(s) == 0) { - errx(EXIT_FAILURE, "safe_atoi(): String is empty"); - } - - for (i = 0; i < strlen(s); ++i) { - if (!isdigit(s[i])) { - errx(EXIT_FAILURE, "safe_atoi(): Non-numeric characters found in string '%s'", s); - } - } - - return atoi(s); -} - -void parse_argv(int argc, char **argv, int *count, int *allocated_memory_size_in_mb, int *allocated_memory_usage_ratio, int *test_mode) { - const struct option long_options[] = { - {"count", 1, 0, 1}, - {"memsize", 1, 0, 2}, - {"ratio", 1, 0, 3}, - {"mode", 1, 0, 4}, - {0, 0, 0, 0} - }; - int c; - int usage = 0; - int got[4]; - int optarg_int; - - memset(&got, 0, sizeof(got)); - while (1) { - c = getopt_long(argc, argv, "", &long_options[0], NULL); - if (c == -1) { // no more arguments - break; - } - - if (c >= 1 && c <= sizeof(got)/sizeof(int)) { - got[c-1] = 1; - } - - if (!optarg) { - warnx("You provided no value"); - usage = 1; - break; - } - optarg_int = safe_atoi(optarg); - - switch (c) { - case 1: - *count = optarg_int; - break; - case 2: - *allocated_memory_size_in_mb = optarg_int; - break; - case 3: - *allocated_memory_usage_ratio = optarg_int; - break; - case 4: - *test_mode = optarg_int; - break; - default: - warnx("Bad option"); - usage = 1; - break; - } - - if (usage) { - break; - } - } - - for (c = 0; c < sizeof(got)/sizeof(int); ++c) { - if (!got[c]) { - warnx("Option #%d not specified", c); - usage = 1; - } - } - - if (usage) { - warnx("Usage: %s ...options - all are required...\n", argv[0]); - warnx("\t--count\n\t--memsize [MBytes]\n\t--ratio [0..N, 0=no_usage_of_memory]\n\t--mode [0..7]\n"); - exit(EXIT_FAILURE); - } -} - -int main(int argc, char **argv) { - int count; - char *m; - int allocated_memory_size_in_mb; - int allocated_memory_usage_ratio; - int test_mode; - int wrote = 0; - - count = 30000; - allocated_memory_size_in_mb = 20; - allocated_memory_usage_ratio = 2; /* the memory usage is 1 divided by the "allocated_memory_usage_ratio", use 0 for "no usage" at all */ - test_mode = 5; - - parse_argv(argc, argv, &count, &allocated_memory_size_in_mb, &allocated_memory_usage_ratio, &test_mode); - - m = allocate_memory(allocated_memory_size_in_mb, allocated_memory_usage_ratio); - - warnx("Test options: count=%d, memsize=%d, ratio=%d, mode=%d", count, allocated_memory_size_in_mb, allocated_memory_usage_ratio, test_mode); - - while (count--) { - switch (test_mode) { - /* the following fork + exec calls do not return the output of their commands */ - case 0: - if (!wrote) warnx("fork() + exec(), standard Libc"); - fork_test(1); - break; - case 1: - if (!wrote) warnx("vfork() + exec(), standard Libc"); - fork_test(0); - break; - case 2: - if (!wrote) warnx("system(), standard Libc"); - system("./tiny2 >/dev/null"); - break; - - /* all the below popen() use-cases are tested if they return the correct string in *fp */ - case 3: - if (!wrote) warnx("popen(), standard Libc"); - popen_test(USE_LIBC_POPEN); - break; - case 4: - use_noshell_compat = 0; - if (!wrote) warnx("the new noshell, debug fork(), compat=%d", use_noshell_compat); - popen_noshell_set_fork_mode(POPEN_NOSHELL_MODE_FORK); - popen_test(USE_NOSHELL_POPEN); - break; - case 5: - use_noshell_compat = 0; - if (!wrote) warnx("the new noshell, default vfork(), compat=%d", use_noshell_compat); - popen_noshell_set_fork_mode(POPEN_NOSHELL_MODE_CLONE); - popen_test(USE_NOSHELL_POPEN); - break; - case 6: - use_noshell_compat = 1; - if (!wrote) warnx("the new noshell, debug fork(), compat=%d", use_noshell_compat); - popen_noshell_set_fork_mode(POPEN_NOSHELL_MODE_FORK); - popen_test(USE_NOSHELL_POPEN); - break; - case 7: - use_noshell_compat = 1; - if (!wrote) warnx("the new noshell, default vfork(), compat=%d", use_noshell_compat); - popen_noshell_set_fork_mode(POPEN_NOSHELL_MODE_CLONE); - popen_test(USE_NOSHELL_POPEN); - break; - default: - errx(EXIT_FAILURE, "Bad mode"); - break; - } - wrote = 1; - } - - return 0; -} diff --git a/third-party/popen-noshell/performance_tests/popen_noshell.c b/third-party/popen-noshell/performance_tests/popen_noshell.c deleted file mode 120000 index 10c53de12..000000000 --- a/third-party/popen-noshell/performance_tests/popen_noshell.c +++ /dev/null @@ -1 +0,0 @@ -../popen_noshell.c \ No newline at end of file diff --git a/third-party/popen-noshell/performance_tests/popen_noshell.h b/third-party/popen-noshell/performance_tests/popen_noshell.h deleted file mode 120000 index 4dc8458f3..000000000 --- a/third-party/popen-noshell/performance_tests/popen_noshell.h +++ /dev/null @@ -1 +0,0 @@ -../popen_noshell.h \ No newline at end of file diff --git a/third-party/popen-noshell/performance_tests/run-tests.pl b/third-party/popen-noshell/performance_tests/run-tests.pl deleted file mode 100755 index 00a202813..000000000 --- a/third-party/popen-noshell/performance_tests/run-tests.pl +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/perl -# popen_noshell: A faster implementation of popen() and system() for Linux. -# Copyright (c) 2009 Ivan Zahariev (famzah) -# Version: 1.0 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; under version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -use strict; -use warnings; - -use Data::Dumper; - -my $count = 10000; -my $memsize = 20; -my $ratio = 2; -my $allowed_deviation = 20; # +/- percent -my $repeat_tests = 3; -my $uname_args = '-s -r -m'; # or just "-a" :) - -my $s; -my @lines; -my $line; -my $i; -my $options; -my $caption; -my $user_t; -my $sys_t; -my %results; -my $k; -my $mode; -my $avg_user_t; -my $avg_sys_t; -my $success; -my @sheet = (); - -sub parse_die($$) { - my ($i, $line) = @_; - die("Unable to parse line $i: $line"); -} - -if ($repeat_tests < 2) { - die('repeat_tests must be at least 2'); -} - -$options = undef; -print "The tests are being performed, this will take some time...\n\n"; -for $mode (0..7) { - print(('-'x80)."\n\n"); - for (1..$repeat_tests) { - $s = `gcc -Wall fork-performance.c popen_noshell.c -o fork-performance && time ./fork-performance --count=$count --memsize=$memsize --ratio=$ratio --mode=$mode 2>&1 >/dev/null`; - print "$s\n"; - - @lines = split(/\n/, $s); - $i = 0; - $caption = $user_t = $sys_t = undef; - foreach $line (@lines) { - ++$i; - if ($i == 1) { - if ($line =~ /^fork-performance: Test options: (.+), mode=\d+$/) { - if (!defined($options)) { - $options = $1; - } else { - if ($options ne $1) { - die("Parsed options is not the same: $options vs. $1"); - } - } - } else { - parse_die($i, $line); - } - } elsif ($i == 2) { - if ($line =~ /^fork-performance: (.+)$/) { - $caption = $1; - } else { - parse_die($i, $line); - } - } elsif ($i == 3) { - if ($line =~ /(\d+.\d+)\s?user\s+(\d+.\d+)\s?sys(?:tem)?/) { - $user_t = $1; - $sys_t = $2; - } else { - parse_die($i, $line); - } - } elsif ($i == 4) { - # noop - } else { - parse_die($i, $line); - } - } - if (!defined($options) || !defined($caption) || !defined($user_t) || !defined($sys_t)) { - die('Parsing failed'); - } - $k = $caption; - if (!exists($results{$k})) { - $results{$k} = []; - } - push(@{$results{$k}}, [$user_t, $sys_t]); - } -} - -sub print_deviation($$$) { - my ($label, $val, $avg_val) = @_; - my $deviation; - my $retval = 1; - - if ($avg_val == 0) { - $deviation = '?? '; - $retval = 0; - } else { - $deviation = (($val / $avg_val) - 1) * 100; - $deviation = sprintf('%.0f', $deviation); - if (abs($deviation) > $allowed_deviation) { - $retval = 0; - } - } - - printf("\t%s: %s (%4s%%)%s\n", $label, $val, $deviation, ($retval == 0 ? ' BAD VALUE' : '')); - - return $retval; -} - -$success = 1; -print("\n\n".('-'x80)."\n"); -print "RAW PERFORMANCE TESTS RESULTS\n"; -print(('-'x80)."\n"); -foreach $k (keys %results) { - $avg_user_t = 0; - $avg_sys_t = 0; - $i = 0; - foreach (@{$results{$k}}) { - ($user_t, $sys_t) = @{$_}; - $avg_user_t += $user_t; - $avg_sys_t += $sys_t; - ++$i; - } - if ($i != $repeat_tests) { - die("Sanity check failed for count: $count vs. $i"); - } - $avg_user_t /= $i; - $avg_sys_t /= $i; - $avg_user_t = sprintf('%.2f', $avg_user_t); - $avg_sys_t = sprintf('%.2f', $avg_sys_t); - - $s = sprintf("%s | avg_user_t | %s | avg_sys_t | %s | total_t | %s\n", $k, $avg_user_t, $avg_sys_t, ($avg_user_t + $avg_sys_t)); - push(@sheet, $s); - print $s; - - foreach (@{$results{$k}}) { - ($user_t, $sys_t) = @{$_}; - - $success &= print_deviation('user_t', $user_t, $avg_user_t); - $success &= print_deviation('sys_t ', $sys_t, $avg_sys_t); - } -} - -print("\n\n".('-'x80)."\n"); -print "PERFORMANCE TESTS REPORT\n"; -print(('-'x80)."\n"); -print "System and setup:\n\t | ".`uname $uname_args`."\t | $options\n"; -print "Here is the data for the graphs:\n"; -foreach (@sheet) { - print "\t | ".$_; -} -print(('-'x80)."\n"); - -if (!$success) { - print "\nWARNING! Some of the measurements were not accurate enough!\n"; - print "It is recommended that you re-run the test having the following in mind:\n"; - print "\t* the machine must be idle and not busy with other tasks\n"; - print "\t* increase the 'count' to a larger number to have more accurate results\n"; - print "\t* it is recommended that 'count' is so big, that the user/sys average time is at least bigger than 1.00\n"; -} diff --git a/third-party/popen-noshell/performance_tests/tiny/freebsd/make.sh b/third-party/popen-noshell/performance_tests/tiny/freebsd/make.sh deleted file mode 100755 index 2cbc39355..000000000 --- a/third-party/popen-noshell/performance_tests/tiny/freebsd/make.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -nasm -f elf tiny.asm -ld -s -o tiny2 tiny.o diff --git a/third-party/popen-noshell/performance_tests/tiny/freebsd/system.inc b/third-party/popen-noshell/performance_tests/tiny/freebsd/system.inc deleted file mode 100644 index d2345d750..000000000 --- a/third-party/popen-noshell/performance_tests/tiny/freebsd/system.inc +++ /dev/null @@ -1,37 +0,0 @@ -%define stdin 0 -%define stdout 1 -%define stderr 2 - -%define SYS_nosys 0 -%define SYS_exit 1 -%define SYS_fork 2 -%define SYS_read 3 -%define SYS_write 4 - -section .text -align 4 -access.the.bsd.kernel: - int 80h - ret - -%macro system 1 - mov eax, %1 - call access.the.bsd.kernel -%endmacro - -%macro sys.exit 0 - system SYS_exit -%endmacro - -%macro sys.fork 0 - system SYS_fork -%endmacro - -%macro sys.read 0 - system SYS_read -%endmacro - -%macro sys.write 0 - system SYS_write -%endmacro - diff --git a/third-party/popen-noshell/performance_tests/tiny/freebsd/tiny.asm b/third-party/popen-noshell/performance_tests/tiny/freebsd/tiny.asm deleted file mode 100644 index ef9070466..000000000 --- a/third-party/popen-noshell/performance_tests/tiny/freebsd/tiny.asm +++ /dev/null @@ -1,18 +0,0 @@ -; http://www.freebsd.org/doc/en/books/developers-handbook/x86-first-program.html - - %include 'system.inc' - -section .data -hello db 'Hello, world!', 0Ah -hbytes equ $-hello - -section .text -global _start -_start: -push dword hbytes -push dword hello -push dword stdout -sys.write - -push dword 0 -sys.exit diff --git a/third-party/popen-noshell/performance_tests/tiny/freebsd/tiny2 b/third-party/popen-noshell/performance_tests/tiny/freebsd/tiny2 deleted file mode 100755 index 93656c51b..000000000 Binary files a/third-party/popen-noshell/performance_tests/tiny/freebsd/tiny2 and /dev/null differ diff --git a/third-party/popen-noshell/performance_tests/tiny/linux/make.sh b/third-party/popen-noshell/performance_tests/tiny/linux/make.sh deleted file mode 100755 index 4524164c6..000000000 --- a/third-party/popen-noshell/performance_tests/tiny/linux/make.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -nasm -f elf tiny2.asm && ld -s -o tiny2 tiny2.o diff --git a/third-party/popen-noshell/performance_tests/tiny/linux/tiny2 b/third-party/popen-noshell/performance_tests/tiny/linux/tiny2 deleted file mode 100755 index 3893c34ce..000000000 Binary files a/third-party/popen-noshell/performance_tests/tiny/linux/tiny2 and /dev/null differ diff --git a/third-party/popen-noshell/performance_tests/tiny/linux/tiny2.asm b/third-party/popen-noshell/performance_tests/tiny/linux/tiny2.asm deleted file mode 100644 index 99d17548a..000000000 --- a/third-party/popen-noshell/performance_tests/tiny/linux/tiny2.asm +++ /dev/null @@ -1,30 +0,0 @@ -; Compile with: nasm -f elf tiny2.asm && ld -s -o tiny2 tiny2.o -; -; Resource: http://www.faqs.org/docs/Linux-HOWTO/Assembly-HOWTO.html#AEN853 - -section .data ;section declaration - -msg db "Hello, world!",0xa ;our dear string -len equ $ - msg ;length of our dear string - -section .text ;section declaration - - ;we must export the entry point to the ELF linker or - global _start ;loader. They conventionally recognize _start as their - ;entry point. Use ld -e foo to override the default. - -_start: - -;write our string to stdout - - mov edx,len ;third argument: message length - mov ecx,msg ;second argument: pointer to message to write - mov ebx,1 ;first argument: file handle (stdout) - mov eax,4 ;system call number (sys_write) - int 0x80 ;call kernel - -;and exit - - mov ebx,0 ;first syscall argument: exit code - mov eax,1 ;system call number (sys_exit) - int 0x80 ;call kernel diff --git a/third-party/popen-noshell/performance_tests/tiny2 b/third-party/popen-noshell/performance_tests/tiny2 deleted file mode 120000 index e22eb9bf7..000000000 --- a/third-party/popen-noshell/performance_tests/tiny2 +++ /dev/null @@ -1 +0,0 @@ -tiny/linux/tiny2 \ No newline at end of file diff --git a/third-party/popen-noshell/popen_noshell.c b/third-party/popen-noshell/popen_noshell.c index 312514429..737c60490 100644 --- a/third-party/popen-noshell/popen_noshell.c +++ b/third-party/popen-noshell/popen_noshell.c @@ -1,6 +1,7 @@ /* * popen_noshell: A faster implementation of popen() and system() for Linux. * Copyright (c) 2009 Ivan Zahariev (famzah) + * Copyright (c) 2012 Gunnar Beutner * Version: 1.0 * * This program is free software; you can redistribute it and/or modify @@ -29,12 +30,6 @@ #include #include -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include -#include - /* * Wish-list: * 1) Make the "ignore_stderr" parameter a mode - ignore, leave unchanged, redirect to stdout (the last is not implemented yet) @@ -55,12 +50,6 @@ _exit(EVAL); \ } -int _popen_noshell_fork_mode = POPEN_NOSHELL_MODE_CLONE; - -void popen_noshell_set_fork_mode(int mode) { // see "popen_noshell.h" POPEN_NOSHELL_MODE_* constants - _popen_noshell_fork_mode = mode; -} - int popen_noshell_reopen_fd_to_dev_null(int fd) { int dev_null_fd; @@ -80,7 +69,7 @@ int popen_noshell_reopen_fd_to_dev_null(int fd) { return 0; } -int _popen_noshell_close_and_dup(int pipefd[2], int closed_pipefd, int target_fd) { +static int popen_noshell_close_and_dup(int pipefd[2], int closed_pipefd, int target_fd) { int dupped_pipefd; dupped_pipefd = (closed_pipefd == 0 ? 1 : 0); // get the FD of the other end of the pipe @@ -102,25 +91,10 @@ int _popen_noshell_close_and_dup(int pipefd[2], int closed_pipefd, int target_fd return 0; } -void _pclose_noshell_free_clone_arg_memory(struct popen_noshell_clone_arg *func_args) { - char **cmd_argv; - - free((char *)func_args->file); - cmd_argv = (char **)func_args->argv; - while (*cmd_argv) { - free(*cmd_argv); - ++cmd_argv; - } - free((char **)func_args->argv); - free(func_args); -} - -void _popen_noshell_child_process( - /* We need the pointer *arg_ptr only to free whatever we reference if exec() fails and we were fork()'ed (thus memory was copied), - * not clone()'d */ - struct popen_noshell_clone_arg *arg_ptr, /* NULL if we were called by pure fork() (not because of Valgrind) */ - int pipefd_0, int pipefd_1, int read_pipe, int ignore_stderr, const char *file, const char * const *argv) { - +static void popen_noshell_child_process( + int pipefd_0, int pipefd_1, + int read_pipe, int ignore_stderr, + const char *file, const char * const *argv) { int closed_child_fd; int closed_pipe_fd; int dupped_child_fd; @@ -142,8 +116,8 @@ void _popen_noshell_child_process( if (popen_noshell_reopen_fd_to_dev_null(closed_child_fd) != 0) { _ERR(255, "popen_noshell_reopen_fd_to_dev_null(%d)", closed_child_fd); } - if (_popen_noshell_close_and_dup(pipefd, closed_pipe_fd, dupped_child_fd) != 0) { - _ERR(255, "_popen_noshell_close_and_dup(%d ,%d)", closed_pipe_fd, dupped_child_fd); + if (popen_noshell_close_and_dup(pipefd, closed_pipe_fd, dupped_child_fd) != 0) { + _ERR(255, "popen_noshell_close_and_dup(%d ,%d)", closed_pipe_fd, dupped_child_fd); } execvp(file, (char * const *)argv); @@ -152,14 +126,6 @@ void _popen_noshell_child_process( warn("exec(\"%s\") inside the child", file); -#ifdef POPEN_NOSHELL_VALGRIND_DEBUG - if (arg_ptr) { /* not NULL if we were called by clone() */ - /* but Valgrind does not support clone(), so we were actually called by fork(), thus memory was copied... */ - /* free this copied memory; if it was not Valgrind, this memory would have been shared and would belong to the parent! */ - _pclose_noshell_free_clone_arg_memory(arg_ptr); - } -#endif - if (fflush(stdout) != 0) _ERR(255, "fflush(stdout)"); if (fflush(stderr) != 0) _ERR(255, "fflush(stderr)"); close(STDIN_FILENO); @@ -169,15 +135,6 @@ void _popen_noshell_child_process( _exit(255); // call _exit() and not exit(), or you'll have troubles in C++ } -int popen_noshell_child_process_by_clone(void *raw_arg) { - struct popen_noshell_clone_arg *arg; - - arg = (struct popen_noshell_clone_arg *)raw_arg; - _popen_noshell_child_process(arg, arg->pipefd_0, arg->pipefd_1, arg->read_pipe, arg->ignore_stderr, arg->file, arg->argv); - - return 0; -} - char ** popen_noshell_copy_argv(const char * const *argv_orig) { int size = 1; /* there is at least one NULL element */ char **argv; @@ -206,69 +163,6 @@ char ** popen_noshell_copy_argv(const char * const *argv_orig) { return argv_new; } -/* - * Similar to vfork() and threading. - * Starts a process which behaves like a thread (shares global variables in memory with the parent) but - * has a different PID and can call exec(), unlike traditional threads which are not allowed to call exec(). - * - * This fork function is very resource-light because it does not copy any memory from the parent, but shares it. - * - * Like standard threads, you have to provide a start function *fn and arguments to it *arg. The life of the - * new vmfork()'ed process starts from this function. - * - * After you have reaped the child via waitpid(), you have to free() the memory at "*memory_to_free_on_child_exit". - * - * When the *fn function returns, the child process terminates. The integer returned by *fn is the exit code for the child process. - * The child process may also terminate explicitly by calling exit(2) or after receiving a fatal signal. - * - * Returns -1 on error. On success returns the PID of the newly created child. - */ -pid_t popen_noshell_vmfork(int (*fn)(void *), void *arg, void **memory_to_free_on_child_exit) { - void *stack, *stack_aligned; - pid_t pid; - - stack = malloc(POPEN_NOSHELL_STACK_SIZE + 15); - if (!stack) return -1; - *memory_to_free_on_child_exit = stack; - - /* - * On all supported Linux platforms the stack grows down, except for HP-PARISC. - * You can grep the kernel source for "STACK_GROWSUP", in order to get this information. - */ - // stack grows down, set pointer at the end of the block - stack_aligned = (void *) ((char * /*byte*/)stack + POPEN_NOSHELL_STACK_SIZE/*bytes*/); - - /* - * On all supported platforms by GNU libc, the stack is aligned to 16 bytes, except for the SuperH platform which is aligned to 8 bytes. - * You can grep the glibc source for "STACK_ALIGN", in order to get this information. - */ - stack_aligned = (void *) ( ((uintptr_t)stack_aligned+15) & ~ 0x0F ); // align to 16 bytes - - /* - * Maybe we could have used posix_memalign() here... - * Our implementation seems a bit more portable though - I've read somewhere that posix_memalign() is not supported on all platforms. - * The above malloc() + align implementation is taken from: - * http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me - */ - -#ifndef POPEN_NOSHELL_VALGRIND_DEBUG - pid = clone(fn, stack_aligned, CLONE_VM | SIGCHLD, arg); -#else - pid = fork(); // Valgrind does not support arbitrary clone() calls, so we use fork for the tests -#endif - if (pid == -1) return -1; - - if (pid == 0) { // child -#ifdef POPEN_NOSHELL_VALGRIND_DEBUG - free(stack); // this is a copy because of the fork(), we are not using it at all - _exit(fn(arg)); // if we used fork() because of Valgrind, invoke the child function manually; always use _exit() -#endif - errx(EXIT_FAILURE, "This must never happen"); - } // child life ends here, for sure - - return pid; -} - /* * Pipe stream to or from process. Similar to popen(), only much faster. * @@ -309,40 +203,12 @@ FILE *popen_noshell(const char *file, const char * const *argv, const char *type if (pipe(pipefd) != 0) return NULL; - if (_popen_noshell_fork_mode) { // use fork() - - pid = fork(); - if (pid == -1) return NULL; - if (pid == 0) { - _popen_noshell_child_process(NULL, pipefd[0], pipefd[1], read_pipe, ignore_stderr, file, argv); - errx(EXIT_FAILURE, "This must never happen"); - } // child life ends here, for sure - - } else { // use clone() - - struct popen_noshell_clone_arg *arg = NULL; - - arg = (struct popen_noshell_clone_arg*) malloc(sizeof(struct popen_noshell_clone_arg)); - if (!arg) return NULL; - - /* Copy memory structures, so that nobody can free() our memory while we use it in the child! */ - arg->pipefd_0 = pipefd[0]; - arg->pipefd_1 = pipefd[1]; - arg->read_pipe = read_pipe; - arg->ignore_stderr = ignore_stderr; - arg->file = strdup(file); - if (!arg->file) return NULL; - arg->argv = (const char * const *)popen_noshell_copy_argv(argv); - if (!arg->argv) return NULL; - - pclose_arg->free_clone_mem = 1; - pclose_arg->func_args = arg; - pclose_arg->stack = NULL; // we will populate it below - - pid = popen_noshell_vmfork(&popen_noshell_child_process_by_clone, arg, &(pclose_arg->stack)); - if (pid == -1) return NULL; - - } // done: using clone() + pid = vfork(); + if (pid == -1) return NULL; + if (pid == 0) { + popen_noshell_child_process(pipefd[0], pipefd[1], read_pipe, ignore_stderr, file, argv); + errx(EXIT_FAILURE, "This must never happen"); + } // child life ends here, for sure /* parent process */ @@ -373,7 +239,7 @@ int popen_noshell_add_ptr_to_argv(char ***argv, int *count, char *start) { return 0; } -int _popen_noshell_add_token(char ***argv, int *count, char *start, char *command, int *j) { +static int popen_noshell_add_token(char ***argv, int *count, char *start, char *command, int *j) { if (start != NULL && command + *j - 1 - start >= 0) { command[*j] = '\0'; // terminate the token in memory *j += 1; @@ -387,7 +253,7 @@ int _popen_noshell_add_token(char ***argv, int *count, char *start, char *comman return 0; } -#define _popen_noshell_split_return_NULL { if (argv != NULL) free(argv); if (command != NULL) free(command); return NULL; } +#define popen_noshell_split_return_NULL { if (argv != NULL) free(argv); if (command != NULL) free(command); return NULL; } char ** popen_noshell_split_command_to_argv(const char *command_original, char **free_this_buf) { char *command; size_t i, len; @@ -395,7 +261,7 @@ char ** popen_noshell_split_command_to_argv(const char *command_original, char * char c; char **argv = NULL; int count = 0; - const char _popen_bash_meta_characters[] = "!\\$`\n|&;()<>"; + const char popen_bash_meta_characters[] = "!\\$`\n|&;()<>"; int in_sq = 0; int in_dq = 0; int j = 0; @@ -404,7 +270,7 @@ char ** popen_noshell_split_command_to_argv(const char *command_original, char * #endif command = (char *)calloc(strlen(command_original) + 1, sizeof(char)); - if (!command) _popen_noshell_split_return_NULL; + if (!command) popen_noshell_split_return_NULL; *free_this_buf = command; @@ -414,9 +280,9 @@ char ** popen_noshell_split_command_to_argv(const char *command_original, char * if (!start) start = command + j; c = command_original[i]; - if (index(_popen_bash_meta_characters, c) != NULL) { + if (index(popen_bash_meta_characters, c) != NULL) { errno = EINVAL; - _popen_noshell_split_return_NULL; + popen_noshell_split_return_NULL; } if (c == ' ' || c == '\t') { @@ -426,8 +292,8 @@ char ** popen_noshell_split_command_to_argv(const char *command_original, char * } // new token - if (_popen_noshell_add_token(&argv, &count, start, command, &j) != 0) { - _popen_noshell_split_return_NULL; + if (popen_noshell_add_token(&argv, &count, start, command, &j) != 0) { + popen_noshell_split_return_NULL; } start = NULL; continue; @@ -446,20 +312,20 @@ char ** popen_noshell_split_command_to_argv(const char *command_original, char * } if (in_sq || in_dq) { // unmatched single/double quote errno = EINVAL; - _popen_noshell_split_return_NULL; + popen_noshell_split_return_NULL; } - if (_popen_noshell_add_token(&argv, &count, start, command, &j) != 0) { - _popen_noshell_split_return_NULL; + if (popen_noshell_add_token(&argv, &count, start, command, &j) != 0) { + popen_noshell_split_return_NULL; } if (count == 0) { errno = EINVAL; - _popen_noshell_split_return_NULL; + popen_noshell_split_return_NULL; } if (popen_noshell_add_ptr_to_argv(&argv, &count, NULL) != 0) { // NULL-terminate the list - _popen_noshell_split_return_NULL; + popen_noshell_split_return_NULL; } #ifdef POPEN_NOSHELL_DEBUG @@ -491,7 +357,7 @@ char ** popen_noshell_split_command_to_argv(const char *command_original, char * * This is simpler than popen_noshell() but is more INSECURE. * Since shells have very complicated expansion, quoting and word splitting algorithms, we do NOT try to re-implement them here. * This function does NOT support any special characters. It will immediately return an error if such symbols are encountered in "command". - * The "command" is split only by space and tab delimiters. The special symbols are pre-defined in _popen_bash_meta_characters[]. + * The "command" is split only by space and tab delimiters. The special symbols are pre-defined in popen_bash_meta_characters[]. * The only special characters supported are single and double quotes. You can enclose arguments in quotes and they should be splitted correctly. * * If possible, use popen_noshell() because of its better security. @@ -542,10 +408,5 @@ int pclose_noshell(struct popen_noshell_pass_to_pclose *arg) { return -1; } - if (arg->free_clone_mem) { - free(arg->stack); - _pclose_noshell_free_clone_arg_memory(arg->func_args); - } - return status; } diff --git a/third-party/popen-noshell/popen_noshell.h b/third-party/popen-noshell/popen_noshell.h index 795dc8ad0..0b47d3a51 100644 --- a/third-party/popen-noshell/popen_noshell.h +++ b/third-party/popen-noshell/popen_noshell.h @@ -1,6 +1,7 @@ /* * popen_noshell: A faster implementation of popen() and system() for Linux. * Copyright (c) 2009 Ivan Zahariev (famzah) + * Copyright (c) 2012 Gunnar Beutner * Version: 1.0 * * This program is free software; you can redistribute it and/or modify @@ -27,28 +28,9 @@ extern "C" { #include #include -/* stack for the child process before it does exec() */ -#define POPEN_NOSHELL_STACK_SIZE 8*1024*1024 /* currently most Linux distros set this to 8 MBytes */ - -/* constants to use with popen_noshell_set_fork_mode() */ -#define POPEN_NOSHELL_MODE_CLONE 0 /* default, faster */ -#define POPEN_NOSHELL_MODE_FORK 1 /* slower */ - -struct popen_noshell_clone_arg { - int pipefd_0; - int pipefd_1; - int read_pipe; - int ignore_stderr; - const char *file; - const char * const *argv; -}; - struct popen_noshell_pass_to_pclose { FILE *fp; pid_t pid; - int free_clone_mem; - void *stack; - struct popen_noshell_clone_arg *func_args; }; /*************************** @@ -64,12 +46,6 @@ FILE *popen_noshell_compat(const char *command, const char *type, struct popen_n /* call this when you have finished reading and writing from/to the child process */ int pclose_noshell(struct popen_noshell_pass_to_pclose *arg); /* the pclose() equivalent */ -/* this is the innovative faster vmfork() which shares memory with the parent and is very resource-light; see the source code for documentation */ -pid_t popen_noshell_vmfork(int (*fn)(void *), void *arg, void **memory_to_free_on_child_exit); - -/* used only for benchmarking purposes */ -void popen_noshell_set_fork_mode(int mode); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/third-party/popen-noshell/popen_noshell_examples.c b/third-party/popen-noshell/popen_noshell_examples.c deleted file mode 100644 index b2b20e40b..000000000 --- a/third-party/popen-noshell/popen_noshell_examples.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * popen_noshell: A faster implementation of popen() and system() for Linux. - * Copyright (c) 2009 Ivan Zahariev (famzah) - * Version: 1.0 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; under version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "popen_noshell.h" -#include -#include -#include -#include -#include - -/*********************************** - * popen_noshell use-case examples * - *********************************** - * - * Compile and test via: - * gcc -Wall popen_noshell.c popen_noshell_examples.c -o popen_noshell_examples && ./popen_noshell_examples - * - * If you want to profile using Valgrind, then compile and run via: - * gcc -Wall -g -DPOPEN_NOSHELL_VALGRIND_DEBUG popen_noshell.c popen_noshell_examples.c -o popen_noshell_examples && \ - * valgrind -q --tool=memcheck --leak-check=yes --show-reachable=yes --track-fds=yes ./popen_noshell_examples - */ - -void satisfy_open_FDs_leak_detection_and_exit() { - /* satisfy Valgrind FDs leak detection for the parent process */ - if (fflush(stdout) != 0) err(EXIT_FAILURE, "fflush(stdout)"); - if (fflush(stderr) != 0) err(EXIT_FAILURE, "fflush(stderr)"); - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - - exit(0); -} - -void example_reading(int use_compat) { - FILE *fp; - char buf[256]; - int status; - struct popen_noshell_pass_to_pclose pclose_arg; - - char *cmd = "ls -la /proc/self/fd"; /* used only by popen_noshell_compat(), we discourage this type of providing a command */ - - /* the command arguments used by popen_noshell() */ - char *exec_file = "ls"; - char *arg1 = "-la"; - char *arg2 = "/proc/self/fd"; - char *arg3 = (char *) NULL; /* last element */ - char *argv[] = {exec_file, arg1, arg2, arg3}; /* NOTE! The first argv[] must be the executed *exec_file itself */ - - if (use_compat) { - fp = popen_noshell_compat(cmd, "r", &pclose_arg); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell_compat()"); - } - } else { - fp = popen_noshell(exec_file, (const char * const *)argv, "r", &pclose_arg, 0); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell()"); - } - } - - while (fgets(buf, sizeof(buf)-1, fp)) { - printf("Got line: %s", buf); - } - - status = pclose_noshell(&pclose_arg); - if (status == -1) { - err(EXIT_FAILURE, "pclose_noshell()"); - } else { - printf("The status of the child is %d. Note that this is not only the exit code. See man waitpid().\n", status); - } -} - -void example_writing(int use_compat) { - FILE *fp; - int status; - struct popen_noshell_pass_to_pclose pclose_arg; - - char *cmd = "tee -a /tmp/popen-noshell.txt"; /* used only by popen_noshell_compat(), we discourage this type of providing a command */ - - /* the command arguments used by popen_noshell() */ - char *exec_file = "tee"; - char *arg1 = "-a"; - char *arg2 = "/tmp/popen-noshell.txt"; - char *arg3 = (char *) NULL; /* last element */ - char *argv[] = {exec_file, arg1, arg2, arg3}; /* NOTE! The first argv[] must be the executed *exec_file itself */ - - if (use_compat) { - fp = popen_noshell_compat(cmd, "w", &pclose_arg); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell_compat()"); - } - } else { - fp = popen_noshell(exec_file, (const char * const *)argv, "w", &pclose_arg, 0); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell()"); - } - } - - if (fprintf(fp, "This is a test line, my pid is %d\n", (int)getpid()) < 0) { - err(EXIT_FAILURE, "fprintf()"); - } - - status = pclose_noshell(&pclose_arg); - if (status == -1) { - err(EXIT_FAILURE, "pclose_noshell()"); - } else { - printf("The status of the child is %d. Note that this is not only the exit code. See man waitpid().\n", status); - } - - printf("Done, you can see the results by executing: cat %s\n", arg2); -} - -int main() { - int try_compat; - int try_read; - - /* - * Tune these options as you need. - */ - try_compat = 0; /* or the more secure, but incompatible version of popen_noshell */ - try_read = 1; /* or write */ - - if (try_read) { - example_reading(try_compat); - } else { - example_writing(try_compat); - } - - satisfy_open_FDs_leak_detection_and_exit(); - - return 0; -} diff --git a/third-party/popen-noshell/popen_noshell_tests.c b/third-party/popen-noshell/popen_noshell_tests.c deleted file mode 100644 index cde0bc6a1..000000000 --- a/third-party/popen-noshell/popen_noshell_tests.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * popen_noshell: A faster implementation of popen() and system() for Linux. - * Copyright (c) 2009 Ivan Zahariev (famzah) - * Version: 1.0 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; under version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "popen_noshell.h" -#include -#include -#include -#include -#include - -/*************************************************** - * popen_noshell C unit test and use-case examples * - *************************************************** - * - * Compile and test via: - * gcc -Wall popen_noshell.c popen_noshell_tests.c -o popen_noshell_tests && ./popen_noshell_tests - * - * Compile for debugging by Valgrind via: - * gcc -Wall -g -DPOPEN_NOSHELL_VALGRIND_DEBUG popen_noshell.c popen_noshell_tests.c -o popen_noshell_tests - * Then start under Valgrind via: - * valgrind -q --tool=memcheck --leak-check=yes --show-reachable=yes --track-fds=yes ./popen_noshell_tests - * If you want to filter Valgrind false reports about 0 opened file descriptors, add the following at the end: - * 2>&1|egrep -v '^==[[:digit:]]{1,5}==( | FILE DESCRIPTORS: 0 open at exit.)$' - */ - -int do_unit_tests_ignore_stderr; - -void satisfy_open_FDs_leak_detection_and_exit() { - /* satisfy Valgrind FDs leak detection for the parent process */ - if (fflush(stdout) != 0) err(EXIT_FAILURE, "fflush(stdout)"); - if (fflush(stderr) != 0) err(EXIT_FAILURE, "fflush(stderr)"); - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - - exit(0); -} - -void assert_string(char *expected, char *got, const char *assert_desc) { - if (strcmp(expected, got) != 0) errx(EXIT_FAILURE, "%s: Expected '%s', got '%s'", assert_desc, expected, got); -} - -void assert_int(int expected, int got, const char *assert_desc) { - if (expected != got) errx(EXIT_FAILURE, "%s: Expected %d, got %d", assert_desc, expected, got); -} - -void assert_status_not_internal_error(int status) { - assert_int(1, status >= 0, "assert_status_not_internal_error"); -} - -void assert_status_signal(int signal, int status) { - assert_status_not_internal_error(status); - assert_int(signal, status & 127, "assert_status_signal"); -} - -void assert_status_exit_code(int code, int status) { - assert_status_not_internal_error(status); - assert_status_signal(0, status); - assert_int(code, status >> 8, "assert_status_exit_code"); -} - -void example_reading(int use_compat) { - FILE *fp; - char buf[256]; - int status; - struct popen_noshell_pass_to_pclose pclose_arg; - - char *cmd = "ls -la /proc/self/fd"; /* used only by popen_noshell_compat(), we discourage this type of providing a command */ - - /* the command arguments used by popen_noshell() */ - char *exec_file = "ls"; - char *arg1 = "-la"; - char *arg2 = "/proc/self/fd"; - char *arg3 = (char *) NULL; /* last element */ - char *argv[] = {exec_file, arg1, arg2, arg3}; /* NOTE! The first argv[] must be the executed *exec_file itself */ - - if (use_compat) { - fp = popen_noshell_compat(cmd, "r", &pclose_arg); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell_compat()"); - } - } else { - fp = popen_noshell(exec_file, (const char * const *)argv, "r", &pclose_arg, 0); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell()"); - } - } - - while (fgets(buf, sizeof(buf)-1, fp)) { - printf("Got line: %s", buf); - } - - status = pclose_noshell(&pclose_arg); - if (status == -1) { - err(EXIT_FAILURE, "pclose_noshell()"); - } else { - printf("The status of the child is %d. Note that this is not only the exit code. See man waitpid().\n", status); - } -} - -void example_writing(int use_compat) { - FILE *fp; - int status; - struct popen_noshell_pass_to_pclose pclose_arg; - - char *cmd = "tee -a /tmp/popen-noshell.txt"; /* used only by popen_noshell_compat(), we discourage this type of providing a command */ - - /* the command arguments used by popen_noshell() */ - char *exec_file = "tee"; - char *arg1 = "-a"; - char *arg2 = "/tmp/popen-noshell.txt"; - char *arg3 = (char *) NULL; /* last element */ - char *argv[] = {exec_file, arg1, arg2, arg3}; /* NOTE! The first argv[] must be the executed *exec_file itself */ - - if (use_compat) { - fp = popen_noshell_compat(cmd, "w", &pclose_arg); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell_compat()"); - } - } else { - fp = popen_noshell(exec_file, (const char * const *)argv, "w", &pclose_arg, 0); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell()"); - } - } - - if (fprintf(fp, "This is a test line, my pid is %d\n", (int)getpid()) < 0) { - err(EXIT_FAILURE, "fprintf()"); - } - - status = pclose_noshell(&pclose_arg); - if (status == -1) { - err(EXIT_FAILURE, "pclose_noshell()"); - } else { - printf("The status of the child is %d. Note that this is not only the exit code. See man waitpid().\n", status); - } - - printf("Done, you can see the results by executing: cat %s\n", arg2); -} - -void unit_test(int reading, char *argv[], char *expected_string, int expected_signal, int expected_exit_code) { - FILE *fp; - char buf[256]; - int status; - struct popen_noshell_pass_to_pclose pclose_arg; - char *received; - size_t received_size; - - fp = popen_noshell(argv[0], (const char * const *)argv, reading ? "r" : "w", &pclose_arg, do_unit_tests_ignore_stderr); - if (!fp) err(EXIT_FAILURE, "popen_noshell"); - - if (reading) { - received_size = strlen(expected_string) + 256; // so that we can store a bit longer strings that we expected and discover the mismatch - received = alloca(received_size); // use alloca() or else the fork()'ed child will generate a Valgrind memory leak warning if exec() fails - if (!received) err(EXIT_FAILURE, "alloca"); - memset(received, 0, received_size); // ensure a terminating null - - while (fgets(buf, sizeof(buf) - 1, fp)) { - strncat(received, buf, received_size - strlen(received) - 2); - } - - assert_string(expected_string, received, "Received string"); - } - - status = pclose_noshell(&pclose_arg); - if (status == -1) { - err(EXIT_FAILURE, "pclose_noshell()"); - } else { - if (expected_signal != 0) { - assert_status_signal(expected_signal, status); - } else { - assert_status_exit_code(expected_exit_code, status); - } - } - - //free(received); // memory allocated by alloca() cannot be free()'d -} - -void do_unit_tests() { - int test_num = 0; - int more_to_test = 1; - char *bin_bash = "/bin/bash"; - char *bin_true = "/bin/true"; - char *bin_cat = "/bin/cat"; - char *bin_echo = "/bin/echo"; - - do { - ++test_num; - switch (test_num) { - case 1: { - char *argv[] = {"/", NULL}; - unit_test(1, argv, "", 0, 255); // failed to execute binary (status code is -1, STDOUT is empty, STDERR text) - break; - } - case 2: { - char *argv[] = {bin_bash, "-c", "ulimit -t 1 && while [ 1 ]; do let COUNTER=COUNTER+1; done;", NULL}; - unit_test(1, argv, "", 9, 5); // process signalled with 9 due to CPU limit (STDOUT/ERR are empty) - break; - } - case 3: { - char *argv[] = {bin_bash, "-c", "sleep 1; exit 1", NULL}; - unit_test(1, argv, "", 0, 1); // process exited with value 1 (STDOUT/ERR are empty) - break; - } - case 4: { - char *argv[] = {bin_bash, "-c", "exit 255", NULL}; - unit_test(1, argv, "", 0, 255); // process exited with value 255 (STDOUT/ERR are empty) - break; - } - case 5: { - char *argv[] = {bin_bash, "-c", "echo \"some err string\" 1>&2; exit 111", NULL}; - unit_test(1, argv, "", 0, 111); // process exited with value 111 (STDERR text, STDOUT is empty) - break; - } - case 6: { - char *argv[] = {bin_bash, "-c", "echo -en \"some err\\nstring v2\" 1>&2; echo -en \"some\\ngood text\"; exit 0", NULL}; - unit_test(1, argv, "some\ngood text", 0, 0); // process exited with value 0 (STDERR text, STDOUT text) - break; - } - case 7: { - char *argv[] = {bin_bash, "-c", "echo -e \"\" 1>&2; echo -e \"\"; exit 3", NULL}; - unit_test(1, argv, "\n", 0, 3); // process exited with value 3 (STDERR text, STDOUT text) - break; - } - case 8: { - char *argv[] = {bin_bash, NULL}; - unit_test(1, argv, "", 0, 0); // process exited with value 0 (single argument, STDOUT/ERR are empty) - break; - } - case 9: { - char *argv[] = {bin_true, NULL}; - unit_test(1, argv, "", 0, 0); // process exited with value 0 (single argument, STDOUT/ERR are empty) - break; - } - case 10: { - char *argv[] = {bin_cat, NULL}; // cat expects an input from STDIN - unit_test(1, argv, "", 0, 0); // process exited with value 0 (single argument, STDOUT/ERR are empty) - break; - } - case 11: { - char *argv[] = {bin_echo, NULL}; - unit_test(1, argv, "\n", 0, 0); // process exited with value 0 (single argument, STDERR is empty, STDOUT text) - break; - } - default: - more_to_test = 0; - break; - } - } while (more_to_test); - - assert_int(11, test_num - 1, "Test count"); -} - -void proceed_to_unit_tests_and_exit() { - popen_noshell_set_fork_mode(POPEN_NOSHELL_MODE_CLONE); /* the default one */ - do_unit_tests(); - popen_noshell_set_fork_mode(POPEN_NOSHELL_MODE_FORK); - do_unit_tests(); - - printf("Tests passed OK.\n"); - - satisfy_open_FDs_leak_detection_and_exit(); -} - -int main() { - do_unit_tests_ignore_stderr = 1; /* do we ignore STDERR from the executed commands? */ - proceed_to_unit_tests_and_exit(); - satisfy_open_FDs_leak_detection_and_exit(); - - return 0; -} diff --git a/third-party/popen-noshell/popen_noshell_tests.cpp b/third-party/popen-noshell/popen_noshell_tests.cpp deleted file mode 100644 index f6ab7541b..000000000 --- a/third-party/popen-noshell/popen_noshell_tests.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * popen_noshell: A faster implementation of popen() and system() for Linux. - * Copyright (c) 2009 Ivan Zahariev (famzah) - * Credits for the C++ test cases go to David Coz - * Version: 1.0 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; under version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "popen_noshell.h" -#include -#include -#include -#include -#include - -/***************************************************** - * popen_noshell C++ unit test and use-case examples * - ***************************************************** - * - * Compile and test via: - * g++ -Wall popen_noshell.c popen_noshell_tests.cpp -o popen_noshell_tests_cpp && ./popen_noshell_tests_cpp - */ - -#define DUMMY_SIZE 10000 - -// Just a dummy structure that allocates memory at startup and releases it -// when exit() is called. - -struct Dummy { - Dummy() { - val = new char[DUMMY_SIZE]; - } - ~Dummy() { - delete[] val; - val = NULL; - } - char* val; -}; - -static Dummy dummy; - -int main() { - FILE *fp; - char buf[256]; - int status; - struct popen_noshell_pass_to_pclose pclose_arg; - - popen_noshell_set_fork_mode(POPEN_NOSHELL_MODE_CLONE); - - // We provide an invalid command, so that the child calls exit(). - // Child's exit() will result in destruction of global objects, while these - // objects belong to the parent! - // Therefore, if the parent uses them after child's exit(), it is likely to - // lead to a crash. - - //char *exec_file = (char *) "ls"; - char *exec_file = (char *) "lsaaa"; - char *arg1 = (char *) "-la"; - char *arg2 = (char *) "/proc/self/fd"; - char *arg3 = (char *) NULL; /* last element */ - char *argv[] = {exec_file, arg1, arg2, arg3}; /* NOTE! The first argv[] must be the executed *exec_file itself */ - - fp = popen_noshell(argv[0], (const char * const *)argv, "r", &pclose_arg, 0); - if (!fp) { - err(EXIT_FAILURE, "popen_noshell()"); - } - - while (fgets(buf, sizeof(buf)-1, fp)) { - printf("Got line: %s", buf); - } - - status = pclose_noshell(&pclose_arg); - if (status == -1) { - err(EXIT_FAILURE, "pclose_noshell()"); - } else { - printf("The status of the child is %d. Note that this is not only the exit code. See man waitpid().\n", status); - } - - // Trying to access our global variable stuff. - // If exit() is used in the child process, dummy.val = 0 and we have a crash. - // With _exit(), dummy.val is still valid. - - // printf("Accessing dummy stuff. dummy.val=%p\n", dummy.val); - memset(dummy.val, 42, DUMMY_SIZE); - printf("\nTests passed OK.\n"); - - return 0; -}