On 10/28/2010 08:33 AM, Markus Duft wrote:
[snip]
> i have no idea how we could be able to reliably find a "real" limit on
> interix, other than a configure check which tries to exec until it works...
> however, the check would need to grow the env to the maximum, too.
i came up with a small (haha) test program, which grows both env and arg sizes
(or better: shrinks them until it works).
the output for interix is:
using sysconf...
max_arg: 1048576
env_sz : 3164
testing...
max_env with min_arg (4096): 60416
max_arg with min_env (4096): 60416
max_env with max_arg (60416): 4096
max_arg with max_env (60416): 4096
is max_env & max_arg together ok? NOT OK
is max_env / 2 & max_arg / 2 together ok? ok
---------------------------------------
limits: arg: 30207, env: 30207
(hehe peter, you where right ;) the 64K(+ some overhead somewhere?) limit is
there).
on my linux box the same test yields:
using sysconf...
max_arg: 2097152
env_sz : 3955
testing...
max_env with min_arg (4096): 130048
max_arg with min_env (4096): 131072
max_env with max_arg (131072): 130048
max_arg with max_env (130048): 131072
is max_env & max_arg together ok? ok
---------------------------------------
limits: arg: 131072, env: 130048
the test program source is attached, could you have a look at it, whether you
have some more ideas on this? it's just a quick-hack, so don't expect error
handling, excessive comments, etc. ;p
thanks, markus
[snip]
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/wait.h>
#define MIN_MAX_ARG 4096
#define MIN_MAX_ENV 4096
#define STEPPING 1024
#define TEST_PROG "/bin/echo"
static int verbose = 0;
void fill_data(char* location, int amount) {
char x = 'a';
while(amount-- > 0) {
if(x > 'z') x = 'a';
location[amount] = x++;
}
}
char ** alloc_arr(int sz, int for_env) {
char ** arr = malloc(sizeof(char*) * (for_env == 0 ? 3 : 2));
int data_index = 0;
if(!for_env) {
arr[0] = strdup(TEST_PROG);
sz -= strlen(arr[0]) + 1;
data_index++;
}
arr[data_index] = malloc(sz + 1);
if(!arr[data_index]) {
perror("malloc");
exit(1);
}
fill_data(arr[data_index], sz);
arr[data_index][sz] = 0;
data_index++;
arr[data_index] = NULL;
return arr;
}
void free_arr(char** arr) {
int i = 0;
while(arr[i])
free(arr[i++]);
free(arr);
}
int calc_env_sz(char** env) {
int acc = 0;
while(env && *env) {
acc += strlen(*env) + 1;
++env;
}
return acc;
}
int test_exec_env(int start_sz, int cmd_size) {
char ** arg = alloc_arr(cmd_size, 0);
while(1) {
char ** env = alloc_arr(start_sz, 1);
if(verbose)
printf("trying env with sz: %d\n", start_sz);
if(try_fork_exec(arg, env) == 0) {
free_arr(env);
free_arr(arg);
return start_sz;
}
free_arr(env);
start_sz -= STEPPING;
if(start_sz < 0) {
printf("oups - env size negative!\n");
exit(1);
}
}
}
int test_exec_arg(int start_sz, int env_sz, int once) {
char ** env = alloc_arr(env_sz, 1);
while(1) {
char ** cmd = alloc_arr(start_sz, 0);
if(verbose)
printf("trying cmd with sz: %d\n", start_sz);
if(try_fork_exec(cmd, env) == 0) {
free_arr(cmd);
free_arr(env);
return start_sz;
}
free_arr(cmd);
start_sz -= STEPPING;
/* didn't work out */
if(once) return -1;
if(start_sz < 0) {
printf("oups - arg size negative!\n");
exit(1);
}
}
}
int try_fork_exec(char ** argv, char** env) {
int pid = fork();
switch(pid) {
case 0: {
/* child */
int new_so = open("/dev/null", O_WRONLY);
if(new_so == -1) {
perror("open");
exit(2);
}
if(dup2(new_so, fileno(stdout)) == -1) {
perror("dup2");
exit(2);
}
execve(argv[0], argv, env);
if(verbose)
perror("exec");
exit(3);
break;
}
case -1:
/* error */
perror("fork");
exit(1);
break;
default: {
/* parent */
int status;
waitpid(pid, &status, 0);
/* exited */
if(!WIFEXITED(status)) {
perror("waitpid");
exit(1);
}
return WEXITSTATUS(status);
}
}
}
int main(int argc, char** argv) {
int max_arg = -1;
int max_env = -1;
#ifdef _SC_ARG_MAX
printf("using sysconf...\n");
max_arg = sysconf(_SC_ARG_MAX);
#endif
#ifdef ARG_MAX
if(max_arg == -1) {
printf("using ARG_MAX...\n");
max_arg = ARG_MAX;
}
#endif
if(max_arg == -1) {
printf("using fallback...\n");
max_arg = 4096;
}
printf("max_arg: %d\n", max_arg);
#if 1
extern char** environ;
printf("env_sz : %d\n", calc_env_sz(environ));
#endif
max_env = max_arg;
printf("testing...\n");
max_env = test_exec_env(max_env, MIN_MAX_ARG);
printf("max_env with min_arg (%d): %d\n", MIN_MAX_ARG, max_env);
max_arg = test_exec_arg(max_arg, MIN_MAX_ENV, 0);
printf("max_arg with min_env (%d): %d\n", MIN_MAX_ENV, max_arg);
printf("max_env with max_arg (%d): %d\n", max_arg, test_exec_env(max_env, max_arg));
printf("max_arg with max_env (%d): %d\n", max_env, test_exec_arg(max_arg, max_env, 0));
{
int tmp;
tmp = test_exec_arg(max_arg, max_env, 1);
printf("is max_env & max_arg together ok? %s\n", tmp == -1 ? "NOT OK" : "ok");
if(tmp == -1) {
tmp = test_exec_arg((max_arg / 2) - 1, (max_env / 2) - 1, 1);
printf("is max_env / 2 & max_arg / 2 together ok? %s\n", tmp == -1 ? "NOT OK" : "ok");
if(tmp == -1) {
tmp = test_exec_arg((max_arg / 4) - 1, (max_env / 4) - 1, 1);
printf("is max_env / 4 & max_arg / 4 together ok? %s\n", tmp == -1 ? "NOT OK" : "ok");
if(tmp == -1) {
printf("failed to find reasonable limits! giving up...\n");
exit(1);
} else {
max_arg = (max_arg / 4) - 1;
max_env = (max_env / 4) - 1;
}
} else {
max_arg = (max_arg / 2) - 1;
max_env = (max_env / 2) - 1;
}
}
}
printf("---------------------------------------\n");
printf("limits: arg: %d, env: %d\n", max_arg, max_env);
return 0;
}