1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 /* The arguments passed to main(). */
11 char  **nxt_process_argv;
12 
13 /*
14  * MacOSX environ(7):
15  *
16  *   Shared libraries and bundles don't have direct access to environ,
17  *   which is only available to the loader ld(1) when a complete program
18  *   is being linked.
19  *
20  * So nxt_process_environ contains an address of environ to allow
21  * change environ[] placement.
22  */
23 char  ***nxt_process_environ;
24 
25 
26 #if (NXT_SETPROCTITLE_ARGV)
27 
28 /*
29  * A process title on Linux, Solaris, and MacOSX can be changed by
30  * copying a new title to a place where the program argument argv[0]
31  * points originally to.  However, the argv[0] may be too small to hold
32  * the new title.  Fortunately, these OSes place the program argument
33  * argv[] strings and the environment environ[] strings contiguously
34  * and their space can be used for the long new process title.
35  *
36  * Solaris "ps" command shows the new title only if it is run in
37  * UCB mode: either "/usr/ucb/ps -axwww" or "/usr/bin/ps axwww".
38  */
39 
40 
41 static u_char  *nxt_process_title_start;
42 static u_char  *nxt_process_title_end;
43 
44 
45 void
nxt_process_arguments(nxt_task_t * task,char ** orig_argv,char *** orig_envp)46 nxt_process_arguments(nxt_task_t *task, char **orig_argv, char ***orig_envp)
47 {
48     u_char      *p, *end, *argv_end, **argv, **env;
49     size_t      size, argv_size, environ_size, strings_size;
50     nxt_uint_t  i;
51 
52     nxt_process_argv = orig_argv;
53     nxt_process_environ = orig_envp;
54 
55     if (orig_envp == NULL) {
56         return;
57     }
58 
59     /*
60      * Set a conservative title space for a case if program argument
61      * strings and environment strings are not contiguous.
62      */
63     argv = (u_char **) orig_argv;
64     nxt_process_title_start = argv[0];
65     nxt_process_title_end = argv[0] + nxt_strlen(argv[0]);
66 
67     end = argv[0];
68     strings_size = 0;
69     argv_size = sizeof(void *);
70 
71     for (i = 0; argv[i] != NULL; i++) {
72         argv_size += sizeof(void *);
73 
74         if (argv[i] == end) {
75             /* Argument strings are contiguous. */
76             size = nxt_strlen(argv[i]) + 1;
77             strings_size += size;
78             end = argv[i] + size;
79         }
80     }
81 
82     argv = nxt_malloc(argv_size);
83     if (argv == NULL) {
84         return;
85     }
86 
87     /*
88      * Copy the entire original argv[] array.  The elements of this array
89      * can point to copied strings or if original argument strings are not
90      * contiguous, to the original argument strings.
91      */
92     nxt_memcpy(argv, orig_argv, argv_size);
93 
94     /*
95      * The argv[1] must be set to NULL on Solaris otherwise the "ps"
96      * command outputs strings pointed by original argv[] elements.
97      * The original argv[] array has always at least two elements so
98      * it is safe to set argv[1].
99      */
100     orig_argv[1] = NULL;
101 
102     nxt_process_argv = (char **) argv;
103 
104     argv_end = end;
105     env = (u_char **) *orig_envp;
106     environ_size = sizeof(void *);
107 
108     for (i = 0; env[i] != NULL; i++) {
109         environ_size += sizeof(void *);
110 
111         if (env[i] == end) {
112             /* Environment strings are contiguous. */
113             size = nxt_strlen(env[i]) + 1;
114             strings_size += size;
115             end = env[i] + size;
116         }
117     }
118 
119     p = nxt_malloc(strings_size);
120     if (p == NULL) {
121         return;
122     }
123 
124     if (argv_end == end) {
125         /*
126          * There is no reason to modify environ if arguments
127          * and environment are not contiguous.
128          */
129         nxt_debug(task, "arguments and environment are not contiguous");
130         goto done;
131     }
132 
133     end = argv[0];
134 
135     for (i = 0; argv[i] != NULL; i++) {
136 
137         if (argv[i] != end) {
138             /* Argument strings are not contiguous. */
139             goto done;
140         }
141 
142         size = nxt_strlen(argv[i]) + 1;
143         nxt_memcpy(p, argv[i], size);
144 
145         end = argv[i] + size;
146         argv[i] = p;
147         p += size;
148     }
149 
150     env = nxt_malloc(environ_size);
151     if (env == NULL) {
152         return;
153     }
154 
155     /*
156      * Copy the entire original environ[] array.  The elements of
157      * this array can point to copied strings or if original environ
158      * strings are not contiguous, to the original environ strings.
159      */
160     nxt_memcpy(env, *orig_envp, environ_size);
161 
162     /* Set the global environ variable to the new array. */
163     *orig_envp = (char **) env;
164 
165     for (i = 0; env[i] != NULL; i++) {
166 
167         if (env[i] != end) {
168             /* Environment strings are not contiguous. */
169             goto done;
170         }
171 
172         size = nxt_strlen(env[i]) + 1;
173         nxt_memcpy(p, env[i], size);
174 
175         end = env[i] + size;
176         env[i] = p;
177         p += size;
178     }
179 
180 done:
181 
182     /* Preserve space for the trailing zero. */
183     end--;
184 
185     nxt_process_title_end = end;
186 }
187 
188 
189 void
nxt_process_title(nxt_task_t * task,const char * fmt,...)190 nxt_process_title(nxt_task_t *task, const char *fmt, ...)
191 {
192     u_char   *p, *start, *end;
193     va_list  args;
194 
195     start = nxt_process_title_start;
196 
197     if (start == NULL) {
198         return;
199     }
200 
201     end = nxt_process_title_end;
202 
203     va_start(args, fmt);
204     p = nxt_vsprintf(start, end, fmt, args);
205     va_end(args);
206 
207 #if (NXT_SOLARIS)
208     /*
209      * Solaris "ps" command shows a new process title only if it is
210      * longer than original command line.  A simple workaround is just
211      * to append the original command line in parenthesis to the title.
212      */
213     {
214         size_t      size;
215         nxt_uint_t  i;
216 
217         size = 0;
218 
219         for (i = 0; nxt_process_argv[i] != NULL; i++) {
220             size += nxt_strlen(nxt_process_argv[i]);
221         }
222 
223         if (size > (size_t) (p - start)) {
224 
225             p = nxt_sprintf(p, end, " (");
226 
227             for (i = 0; nxt_process_argv[i] != NULL; i++) {
228                 p = nxt_sprintf(p, end, "%s ", nxt_process_argv[i]);
229             }
230 
231             if (*(p - 1) == ' ') {
232                 *(p - 1) = ')';
233             }
234         }
235     }
236 #endif
237 
238     /*
239      * A process title must be padded with zeros on MacOSX.  Otherwise
240      * the "ps" command may output parts of environment strings.
241      */
242     nxt_memset(p, '\0', end - p);
243 
244     nxt_debug(task, "setproctitle: \"%s\"", start);
245 }
246 
247 #else /* !(NXT_SETPROCTITLE_ARGV) */
248 
249 void
nxt_process_arguments(nxt_task_t * task,char ** orig_argv,char *** orig_envp)250 nxt_process_arguments(nxt_task_t *task, char **orig_argv, char ***orig_envp)
251 {
252     nxt_process_argv = orig_argv;
253     nxt_process_environ = orig_envp;
254 }
255 
256 #endif
257