/* * Copyright © 2010 William Ahern * Copyright © 2012-2013 Guillem Jover * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "local-link.h" #if !HAVE_DECL_ENVIRON extern char **environ; #endif static struct { /* Original value. */ const char *arg0; /* Title space available. */ char *base, *end; /* Pointer to original nul character within base. */ char *nul; bool warned; bool reset; int error; } SPT; static inline size_t spt_min(size_t a, size_t b) { return a < b ? a : b; } /* * For discussion on the portability of the various methods, see * https://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html */ static int spt_clearenv(void) { #ifdef HAVE_CLEARENV return clearenv(); #else char **tmp; tmp = malloc(sizeof(*tmp)); if (tmp == NULL) return errno; tmp[0] = NULL; environ = tmp; return 0; #endif } static int spt_copyenv(int envc, char *envp[]) { char **envcopy; char *eq; int envsize; int i, error; if (environ != envp) return 0; /* Make a copy of the old environ array of pointers, in case * clearenv() or setenv() is implemented to free the internal * environ array, because we will need to access the old environ * contents to make the new copy. */ envsize = (envc + 1) * sizeof(char *); envcopy = malloc(envsize); if (envcopy == NULL) return errno; memcpy(envcopy, envp, envsize); error = spt_clearenv(); if (error) { environ = envp; free(envcopy); return error; } for (i = 0; envcopy[i]; i++) { eq = strchr(envcopy[i], '='); if (eq == NULL) continue; *eq = '\0'; if (setenv(envcopy[i], eq + 1, 1) < 0) error = errno; *eq = '='; if (error) { #ifdef HAVE_CLEARENV /* Because the old environ might not be available * anymore we will make do with the shallow copy. */ environ = envcopy; #else environ = envp; free(envcopy); #endif return error; } } /* Dispose of the shallow copy, now that we've finished transfering * the old environment. */ free(envcopy); return 0; } static int spt_copyargs(int argc, char *argv[]) { char *tmp; int i; for (i = 1; i < argc || (i >= argc && argv[i]); i++) { if (argv[i] == NULL) continue; tmp = strdup(argv[i]); if (tmp == NULL) return errno; argv[i] = tmp; } return 0; } void setproctitle_init(int argc, char *argv[], char *envp[]) { char *base, *end, *nul, *tmp; int i, envc, error; /* Try to make sure we got called with main() arguments. */ if (argc < 0) return; base = argv[0]; if (base == NULL) return; nul = &base[strlen(base)]; end = nul + 1; for (i = 0; i < argc || (i >= argc && argv[i]); i++) { if (argv[i] == NULL || argv[i] != end) continue; end = argv[i] + strlen(argv[i]) + 1; } for (i = 0; envp[i]; i++) { if (envp[i] != end) continue; end = envp[i] + strlen(envp[i]) + 1; } envc = i; SPT.arg0 = strdup(argv[0]); if (SPT.arg0 == NULL) { SPT.error = errno; return; } tmp = strdup(getprogname()); if (tmp == NULL) { SPT.error = errno; return; } setprogname(tmp); error = spt_copyenv(envc, envp); if (error) { SPT.error = error; return; } error = spt_copyargs(argc, argv); if (error) { SPT.error = error; return; } SPT.nul = nul; SPT.base = base; SPT.end = end; } #ifndef SPT_MAXTITLE #define SPT_MAXTITLE 255 #endif __printflike(1, 2) void setproctitle_impl(const char *fmt, ...); void setproctitle_impl(const char *fmt, ...) { /* Use buffer in case argv[0] is passed. */ char buf[SPT_MAXTITLE + 1]; va_list ap; char *nul; int len; if (SPT.base == NULL) { if (!SPT.warned) { warnx("setproctitle not initialized, please either call " "setproctitle_init() or link against libbsd-ctor."); SPT.warned = true; } return; } if (fmt) { if (fmt[0] == '-') { /* Skip program name prefix. */ fmt++; len = 0; } else { /* Print program name heading for grep. */ snprintf(buf, sizeof(buf), "%s: ", getprogname()); len = strlen(buf); } va_start(ap, fmt); len += vsnprintf(buf + len, sizeof(buf) - len, fmt, ap); va_end(ap); } else { len = snprintf(buf, sizeof(buf), "%s", SPT.arg0); } if (len <= 0) { SPT.error = errno; return; } if (!SPT.reset) { memset(SPT.base, 0, SPT.end - SPT.base); SPT.reset = true; } else { memset(SPT.base, 0, spt_min(sizeof(buf), SPT.end - SPT.base)); } len = spt_min(len, spt_min(sizeof(buf), SPT.end - SPT.base) - 1); memcpy(SPT.base, buf, len); nul = &SPT.base[len]; if (nul < SPT.nul) { *SPT.nul = '.'; } else if (nul == SPT.nul && &nul[1] < SPT.end) { *SPT.nul = ' '; *++nul = '\0'; } } libbsd_symver_default(setproctitle, setproctitle_impl, LIBBSD_0.5); /* The original function introduced in 0.2 was a stub, it only got implemented * in 0.5, make the implementation available in the old version as an alias * for code linking against that version, and change the default to use the * new version, so that new code depends on the implemented version. */ #if defined(libbsd_strong_alias) libbsd_strong_alias(setproctitle_stub, setproctitle_impl); #else void setproctitle_stub(const char *fmt, ...) __attribute__((__alias__("setproctitle_impl"))); #endif libbsd_symver_variant(setproctitle, setproctitle_stub, LIBBSD_0.2);