1/*
2 * Copyright (c) 1995 Peter Wemm <peter@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, is permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice immediately at the beginning of the file, without modification,
10 *    this list of conditions, and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Absolutely no warranty of function or purpose is made by the author
15 *    Peter Wemm.
16 */
17
18#include <sys/cdefs.h>
19__FBSDID("$FreeBSD$");
20
21#include "namespace.h"
22#include <sys/param.h>
23#include <sys/elf_common.h>
24#include <sys/exec.h>
25#include <sys/sysctl.h>
26
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include "un-namespace.h"
32
33#include "libc_private.h"
34
35/*
36 * Older FreeBSD 2.0, 2.1 and 2.2 had different ps_strings structures and
37 * in different locations.
38 * 1: old_ps_strings at the very top of the stack.
39 * 2: old_ps_strings at SPARE_USRSPACE below the top of the stack.
40 * 3: ps_strings at the very top of the stack.
41 * We only support a kernel providing #3 style ps_strings.
42 *
43 * For historical purposes, a definition of the old ps_strings structure
44 * and location is preserved below:
45struct old_ps_strings {
46	char	*old_ps_argvstr;
47	int	old_ps_nargvstr;
48	char	*old_ps_envstr;
49	int	old_ps_nenvstr;
50};
51#define	OLD_PS_STRINGS ((struct old_ps_strings *) \
52	(USRSTACK - SPARE_USRSPACE - sizeof(struct old_ps_strings)))
53 */
54
55#include <stdarg.h>
56
57#define SPT_BUFSIZE 2048	/* from other parts of sendmail */
58
59static char *
60setproctitle_internal(const char *fmt, va_list ap)
61{
62	static struct ps_strings *ps_strings;
63	static char *buf = NULL;
64	static char *obuf = NULL;
65	static char **oargv, *kbuf;
66	static int oargc = -1;
67	static char *nargv[2] = { NULL, NULL };
68	char **nargvp;
69	int nargc;
70	int i;
71	size_t len;
72	unsigned long ul_ps_strings;
73
74	if (buf == NULL) {
75		buf = malloc(SPT_BUFSIZE);
76		if (buf == NULL)
77			return (NULL);
78		nargv[0] = buf;
79	}
80
81	if (obuf == NULL ) {
82		obuf = malloc(SPT_BUFSIZE);
83		if (obuf == NULL)
84			return (NULL);
85		*obuf = '\0';
86	}
87
88	if (fmt) {
89		buf[SPT_BUFSIZE - 1] = '\0';
90
91		if (fmt[0] == '-') {
92			/* skip program name prefix */
93			fmt++;
94			len = 0;
95		} else {
96			/* print program name heading for grep */
97			(void)snprintf(buf, SPT_BUFSIZE, "%s: ", _getprogname());
98			len = strlen(buf);
99		}
100
101		/* print the argument string */
102		(void)vsnprintf(buf + len, SPT_BUFSIZE - len, fmt, ap);
103
104		nargvp = nargv;
105		nargc = 1;
106		kbuf = buf;
107	} else if (*obuf != '\0') {
108		/* Idea from NetBSD - reset the title on fmt == NULL */
109		nargvp = oargv;
110		nargc = oargc;
111		kbuf = obuf;
112	} else
113		/* Nothing to restore */
114		return (NULL);
115
116	if (ps_strings == NULL)
117		(void)_elf_aux_info(AT_PS_STRINGS, &ps_strings,
118		    sizeof(ps_strings));
119
120	if (ps_strings == NULL) {
121		len = sizeof(ul_ps_strings);
122		if (sysctlbyname("kern.ps_strings", &ul_ps_strings, &len, NULL,
123		    0) == -1)
124			return (NULL);
125		ps_strings = (struct ps_strings *)ul_ps_strings;
126	}
127
128	if (ps_strings == NULL)
129		return (NULL);
130
131	/*
132	 * PS_STRINGS points to zeroed memory on a style #2 kernel.
133	 * Should not happen.
134	 */
135	if (ps_strings->ps_argvstr == NULL)
136		return (NULL);
137
138	/* style #3 */
139	if (oargc == -1) {
140		/* Record our original args */
141		oargc = ps_strings->ps_nargvstr;
142		oargv = ps_strings->ps_argvstr;
143		for (i = len = 0; i < oargc; i++) {
144			/*
145			 * The program may have scribbled into its
146			 * argv array, e.g., to remove some arguments.
147			 * If that has happened, break out before
148			 * trying to call strlen on a NULL pointer.
149			 */
150			if (oargv[i] == NULL) {
151				oargc = i;
152				break;
153			}
154			snprintf(obuf + len, SPT_BUFSIZE - len, "%s%s",
155			    len != 0 ? " " : "", oargv[i]);
156			if (len != 0)
157				len++;
158			len += strlen(oargv[i]);
159			if (len >= SPT_BUFSIZE)
160				break;
161		}
162	}
163	ps_strings->ps_nargvstr = nargc;
164	ps_strings->ps_argvstr = nargvp;
165
166	return (nargvp[0]);
167}
168
169static int fast_update = 0;
170
171void
172setproctitle_fast(const char *fmt, ...)
173{
174	va_list ap;
175	char *buf;
176	int oid[4];
177
178	va_start(ap, fmt);
179	buf = setproctitle_internal(fmt, ap);
180	va_end(ap);
181
182	if (buf && !fast_update) {
183		/* Tell the kernel to start looking in user-space */
184		oid[0] = CTL_KERN;
185		oid[1] = KERN_PROC;
186		oid[2] = KERN_PROC_ARGS;
187		oid[3] = -1;
188		sysctl(oid, 4, 0, 0, "", 0);
189		fast_update = 1;
190	}
191}
192
193void
194setproctitle(const char *fmt, ...)
195{
196	va_list ap;
197	char *buf;
198	int oid[4];
199
200	va_start(ap, fmt);
201	buf = setproctitle_internal(fmt, ap);
202	va_end(ap);
203
204	if (buf != NULL) {
205		/* Set the title into the kernel cached command line */
206		oid[0] = CTL_KERN;
207		oid[1] = KERN_PROC;
208		oid[2] = KERN_PROC_ARGS;
209		oid[3] = -1;
210		sysctl(oid, 4, 0, 0, buf, strlen(buf) + 1);
211		fast_update = 0;
212	}
213}
214