/* Return a copy of argv[0] encoded in the default encoding. * * Return a newly allocated buffer to be released with free(). * * Return NULL in case of error. If the error shouldn't be ignored, also set * a Python exception. */ static char * get_encoded_arg0(wchar_t *argv0) { PyObject *ua = NULL, *ba = NULL; char *rv = NULL; if (!(ua = PyUnicode_FromWideChar(argv0, -1))) { spt_debug("failed to convert argv[0] to unicode"); PyErr_Clear(); goto exit; } if (!(ba = PyUnicode_AsEncodedString( ua, PyUnicode_GetDefaultEncoding(), "strict"))) { spt_debug("failed to encode argv[0]"); PyErr_Clear(); goto exit; } if (!(rv = strdup(PyBytes_AsString(ba)))) { PyErr_NoMemory(); } exit: Py_XDECREF(ua); Py_XDECREF(ba); return rv; }
/* Initialize the module internal functions. * * The function reproduces the initialization performed by PostgreSQL * to be able to call the functions in pg_status.c * * Return 0 in case of success, else -1. In case of failure with an error that * shouldn't be ignored, also set a Python exception. * * The function should be called only once in the process lifetime. * so is called at module initialization. After the function is called, * set_ps_display() can be used. */ int spt_setup(void) { int rv = -1; #ifndef WIN32 int argc = 0; char **argv = NULL; char *init_title; if (0 > get_argc_argv(&argc, &argv)) { spt_debug("get_argc_argv failed"); goto exit; } save_ps_display_args(argc, argv); /* Set up the first title to fully initialize the code */ if (!(init_title = join_argv(argc, argv))) { goto exit; } init_ps_display(init_title); free(init_title); #else /* On Windows save_ps_display_args is a no-op * This is a good news, because Py_GetArgcArgv seems not usable. */ LPTSTR init_title = GetCommandLine(); init_ps_display(init_title); #endif rv = 0; exit: return rv; }
/* Initialize the module internal functions. * * The function reproduces the initialization performed by PostgreSQL * to be able to call the functions in pg_status.c * * The function should be called only once in the process lifetime. * so is called at module initialization. After the function is called, * set_ps_display() can be used. */ void spt_setup(void) { #ifndef WIN32 int argc = 0; char **argv = NULL; if (!get_argc_argv(&argc, &argv)) { spt_debug("setup failed"); goto exit; } save_ps_display_args(argc, argv); /* Set up the first title to fully initialize the code */ char *init_title = join_argv(argc, argv); init_ps_display(init_title); free(init_title); #else /* On Windows save_ps_display_args is a no-op * This is a good news, because Py_GetArgcArgv seems not usable. */ LPTSTR init_title = GetCommandLine(); init_ps_display(init_title); #endif exit: /* clear the exception. Propagating it to the module init would make a * fatal error. What we want instead is just to create a no-op module. */ PyErr_Clear(); }
/* Initialize the module internal functions. * * The function reproduces the initialization performed by PostgreSQL * to be able to call the functions in pg_status.c * * The function should be called only once in the process lifetime. * so is called at module initialization. After the function is called, * set_ps_display() can be used. */ void spt_setup(void) { #ifndef WIN32 int argc = 0; char **argv = NULL; if (!get_argc_argv(&argc, &argv)) { spt_debug("setup failed"); return; } save_ps_display_args(argc, argv); /* Set up the first title to fully initialize the code */ char *init_title = join_argv(argc, argv); init_ps_display(init_title); free(init_title); #else /* On Windows save_ps_display_args is a no-op * This is a good news, because Py_GetArgcArgv seems not usable. */ LPTSTR init_title = GetCommandLine(); init_ps_display(init_title); #endif }
/* Come on, why is this missing?! this is just cruel! * I guess you club seal pups for hobby. */ PyObject * PyFile_FromString(const char *filename, const char *mode) { PyObject *io = NULL; PyObject *rv = NULL; if (!(io = PyImport_ImportModule("io"))) { spt_debug("failed to import io"); goto exit; } rv = PyObject_CallMethod(io, "open", "ss", filename, mode); exit: Py_XDECREF(io); return rv; }
/* Find the original arg buffer, return nonzero if found. * * If found, set argc to the number of arguments, argv to an array * of pointers to the single arguments. The array is allocated via malloc. * * The function overcomes two Py_GetArgcArgv shortcomings: * - some python parameters mess up with the original argv, e.g. -m * (see issue #8) * - with Python 3, argv is a decoded copy and doesn't point to * the original area. */ static int get_argc_argv(int *argc, char ***argv) { int rv = false; #ifdef IS_PY3K if (!(rv = find_argv_from_env(argc, argv))) { spt_debug("get_argc_argv failed"); } #else Py_GetArgcArgv(argc, argv); *argv = fix_argv(*argc, *argv); rv = true; #endif return rv; }
/* Find the original arg buffer, return 0 if found, else -1. * * If found, set argc to the number of arguments, argv to an array * of pointers to the single arguments. The array is allocated via malloc. * * If the function fails in a way that shouldn't be ignored, also set * a Python exception. * * The function overcomes three Py_GetArgcArgv shortcomings: * - some python parameters mess up with the original argv, e.g. -m * (see issue #8) * - with Python 3, argv is a decoded copy and doesn't point to * the original area. * - If python is embedded, the function doesn't return anything. */ static int get_argc_argv(int *argc_o, char ***argv_o) { int argc = 0; argv_t **argv_py = NULL; char **argv = NULL; char *arg0 = NULL; int rv = -1; #ifndef IS_PYPY spt_debug("reading argc/argv from Python main"); Py_GetArgcArgv(&argc, &argv_py); #endif if (argc > 0) { spt_debug("found %d arguments", argc); #ifdef IS_PY3K if (!(arg0 = get_encoded_arg0(argv_py[0]))) { spt_debug("couldn't get a copy of argv[0]"); goto exit; } #else if (!(argv = fix_argv(argc, (char **)argv_py))) { spt_debug("failed to fix argv"); goto exit; } #endif /* we got argv: on py2 it points to the right place in memory; on py3 * we only got a copy of argv[0]: we will use it to look from environ */ } else { spt_debug("no good news from Py_GetArgcArgv"); /* get a copy of argv[0] from /proc, so we get back in the same * situation of Py3 */ if (0 > get_args_from_proc(&argc, &arg0)) { spt_debug("failed to get args from proc fs"); goto exit; } } /* If we don't know argv but we know the content of argv[0], we can walk * backwards from environ and see if we get it. */ if (arg0 && !argv) { if (!(argv = find_argv_from_env(argc, arg0))) { spt_debug("couldn't find argv from environ"); goto exit; } } /* success */ *argc_o = argc; *argv_o = argv; argv = NULL; rv = 0; exit: if (arg0) { free(arg0); } if (argv) { free(argv); } return rv; }
/* Read the number of arguments and the first argument from /proc/pid/cmdline * * Return 0 if found, else -1. Return arg0 in a malloc'd array. * * If the function fails in a way that shouldn't be ignored, also set * a Python exception. */ static int get_args_from_proc(int *argc_o, char **arg0_o) { /* allow /proc/PID/cmdline, with oversize max_pid, and them some. */ #define FNLEN 30 char fn[FNLEN]; PyObject *os = NULL; PyObject *pid_py = NULL; long pid; PyObject *f = NULL; PyObject *cl = NULL; PyObject *tmp = NULL; int rv = -1; spt_debug("looking for args into proc fs"); /* get the pid from os.getpid() */ if (!(os = PyImport_ImportModule("os"))) { spt_debug("failed to import os"); goto exit; } if (!(pid_py = PyObject_CallMethod(os, "getpid", NULL))) { spt_debug("calling os.getpid() failed"); /* os.getpid() may be not available, so ignore this error. */ PyErr_Clear(); goto exit; } if (-1 == (pid = PyInt_AsLong(pid_py))) { spt_debug("os.getpid() returned crap?"); /* Don't bother to check PyErr_Occurred as pid can't just be -1. */ goto exit; } /* get the content of /proc/PID/cmdline */ snprintf(fn, FNLEN, "/proc/%ld/cmdline", pid); if (!(f = PyFile_FromString(fn, "rb"))) { spt_debug("opening '%s' failed", fn); /* That's ok: procfs is easily not available on menomated unices */ PyErr_Clear(); goto exit; } /* the file has been open in binary mode, so we get bytes */ cl = PyObject_CallMethod(f, "read", NULL); if (!(tmp = PyObject_CallMethod(f, "close", NULL))) { spt_debug("closing failed"); } else { Py_DECREF(tmp); } if (!cl) { spt_debug("reading failed"); /* could there be some protected environment where a process cannot * read its own pid? Who knows, better not to risk. */ PyErr_Clear(); goto exit; } /* the cmdline is a buffer of null-terminated strings. We can strdup it to * get a copy of arg0, and count the zeros to get argc */ { char *ccl; Py_ssize_t i; if (!(ccl = Bytes_AsString(cl))) { spt_debug("failed to get cmdline string"); goto exit; } if (!(*arg0_o = strdup(ccl))) { spt_debug("arg0 strdup failed"); PyErr_NoMemory(); goto exit; } spt_debug("got argv[0] = '%s' from /proc", *arg0_o); *argc_o = 0; for (i = Bytes_Size(cl) - 1; i >= 0; --i) { if (ccl[i] == '\0') { (*argc_o)++; } } spt_debug("got argc = %d from /proc", *argc_o); } /* success */ rv = 0; exit: Py_XDECREF(cl); Py_XDECREF(f); Py_XDECREF(pid_py); Py_XDECREF(os); return rv; }
/* Find the original arg buffer starting from the env position. * * Return nonzero if found. * * Required on Python 3 as Py_GetArgcArgv doesn't return pointers to the * original area. */ static int find_argv_from_env(int *argc_o, char ***argv_o) { int rv = 0; int argc; wchar_t **argv; char **buf = NULL; char *arg0 = NULL; /* Find the number of parameters. */ Py_GetArgcArgv(&argc, &argv); buf = (char **)malloc((argc + 1) * sizeof(char *)); buf[argc] = NULL; /* Walk back from environ until you find argc-1 null-terminated strings. * Don't look for argv[0] as it's probably not preceded by 0. */ int i; char *ptr = environ[0]; char *limit = ptr - 8192; /* TODO: empiric limit: should use MAX_ARG */ --ptr; for (i = argc - 1; i >= 1; --i) { if (*ptr) { spt_debug("zero %d not found", i); goto error; } --ptr; while (*ptr && ptr > limit) { --ptr; } if (ptr <= limit) { spt_debug("failed to found arg %d start", i); goto error; } buf[i] = (ptr + 1); } /* The first arg has not a zero in front. But what we have is reliable * enough (modulo its encoding). Check if it is exactly what found. * * The check is known to fail on OS X with locale C if there are * non-ascii characters in the executable path. See Python issue #9167 */ arg0 = get_encoded_arg0(argv[0]); if (!arg0) { goto error; } ptr -= strlen(arg0); if (ptr <= limit) { spt_debug("failed to found argv[0] start"); goto error; } if (strcmp(ptr, arg0)) { spt_debug("failed to recognize argv[0]"); goto error; } /* We have all the pieces of the jigsaw. */ buf[0] = ptr; *argc_o = argc; *argv_o = buf; rv = 1; goto exit; error: if (buf) { free(buf); } exit: if (arg0) { free(arg0); } return rv; }