/* * Copyright © 2013 Guillem Jover * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include static struct { /* Original value. */ const char *arg0; /* Title space available. */ char *base, *end; /* Pointer to original nul character within base. */ char *nul; boolean_t warned; boolean_t reset; int error; } SPT; #define LIBBSD_IS_PATHNAME_SEPARATOR(c) ((c) == '/') #define SPT_MAXTITLE 255 extern const char *__progname; static const char * getprogname(void) { return (__progname); } static void setprogname(const char *progname) { size_t i; for (i = strlen(progname); i > 0; i--) { if (LIBBSD_IS_PATHNAME_SEPARATOR(progname[i - 1])) { __progname = progname + i; return; } } __progname = progname; } 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) { char **tmp; tmp = malloc(sizeof (*tmp)); if (tmp == NULL) return (errno); tmp[0] = NULL; environ = tmp; return (0); } 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) { environ = envp; free(envcopy); 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 zfs_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; } void zfs_setproctitle(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" "call zfs_setproctitle_init()"); SPT.warned = B_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 = B_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'; } }