1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <fnmatch.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "tmux.h"
27 
28 /*
29  * Environment - manipulate a set of environment variables.
30  */
31 
32 RB_HEAD(environ, environ_entry);
33 static int environ_cmp(struct environ_entry *, struct environ_entry *);
34 RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp);
35 
36 static int
environ_cmp(struct environ_entry * envent1,struct environ_entry * envent2)37 environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
38 {
39           return (strcmp(envent1->name, envent2->name));
40 }
41 
42 /* Initialise the environment. */
43 struct environ *
environ_create(void)44 environ_create(void)
45 {
46           struct environ      *env;
47 
48           env = xcalloc(1, sizeof *env);
49           RB_INIT(env);
50 
51           return (env);
52 }
53 
54 /* Free an environment. */
55 void
environ_free(struct environ * env)56 environ_free(struct environ *env)
57 {
58           struct environ_entry          *envent, *envent1;
59 
60           RB_FOREACH_SAFE(envent, environ, env, envent1) {
61                     RB_REMOVE(environ, env, envent);
62                     free(envent->name);
63                     free(envent->value);
64                     free(envent);
65           }
66           free(env);
67 }
68 
69 struct environ_entry *
environ_first(struct environ * env)70 environ_first(struct environ *env)
71 {
72           return (RB_MIN(environ, env));
73 }
74 
75 struct environ_entry *
environ_next(struct environ_entry * envent)76 environ_next(struct environ_entry *envent)
77 {
78           return (RB_NEXT(environ, env, envent));
79 }
80 
81 /* Copy one environment into another. */
82 void
environ_copy(struct environ * srcenv,struct environ * dstenv)83 environ_copy(struct environ *srcenv, struct environ *dstenv)
84 {
85           struct environ_entry          *envent;
86 
87           RB_FOREACH(envent, environ, srcenv) {
88                     if (envent->value == NULL)
89                               environ_clear(dstenv, envent->name);
90                     else {
91                               environ_set(dstenv, envent->name, envent->flags,
92                                   "%s", envent->value);
93                     }
94           }
95 }
96 
97 /* Find an environment variable. */
98 struct environ_entry *
environ_find(struct environ * env,const char * name)99 environ_find(struct environ *env, const char *name)
100 {
101           struct environ_entry          envent;
102 
103           envent.name = __UNCONST(name);
104           return (RB_FIND(environ, env, &envent));
105 }
106 
107 /* Set an environment variable. */
108 void
environ_set(struct environ * env,const char * name,int flags,const char * fmt,...)109 environ_set(struct environ *env, const char *name, int flags, const char *fmt,
110     ...)
111 {
112           struct environ_entry          *envent;
113           va_list                        ap;
114 
115           va_start(ap, fmt);
116           if ((envent = environ_find(env, name)) != NULL) {
117                     envent->flags = flags;
118                     free(envent->value);
119                     xvasprintf(&envent->value, fmt, ap);
120           } else {
121                     envent = xmalloc(sizeof *envent);
122                     envent->name = xstrdup(name);
123                     envent->flags = flags;
124                     xvasprintf(&envent->value, fmt, ap);
125                     RB_INSERT(environ, env, envent);
126           }
127           va_end(ap);
128 }
129 
130 /* Clear an environment variable. */
131 void
environ_clear(struct environ * env,const char * name)132 environ_clear(struct environ *env, const char *name)
133 {
134           struct environ_entry          *envent;
135 
136           if ((envent = environ_find(env, name)) != NULL) {
137                     free(envent->value);
138                     envent->value = NULL;
139           } else {
140                     envent = xmalloc(sizeof *envent);
141                     envent->name = xstrdup(name);
142                     envent->flags = 0;
143                     envent->value = NULL;
144                     RB_INSERT(environ, env, envent);
145           }
146 }
147 
148 /* Set an environment variable from a NAME=VALUE string. */
149 void
environ_put(struct environ * env,const char * var,int flags)150 environ_put(struct environ *env, const char *var, int flags)
151 {
152           char      *name, *value;
153 
154           value = strchr(var, '=');
155           if (value == NULL)
156                     return;
157           value++;
158 
159           name = xstrdup(var);
160           name[strcspn(name, "=")] = '\0';
161 
162           environ_set(env, name, flags, "%s", value);
163           free(name);
164 }
165 
166 /* Unset an environment variable. */
167 void
environ_unset(struct environ * env,const char * name)168 environ_unset(struct environ *env, const char *name)
169 {
170           struct environ_entry          *envent;
171 
172           if ((envent = environ_find(env, name)) == NULL)
173                     return;
174           RB_REMOVE(environ, env, envent);
175           free(envent->name);
176           free(envent->value);
177           free(envent);
178 }
179 
180 /* Copy variables from a destination into a source environment. */
181 void
environ_update(struct options * oo,struct environ * src,struct environ * dst)182 environ_update(struct options *oo, struct environ *src, struct environ *dst)
183 {
184           struct environ_entry                    *envent;
185           struct environ_entry                    *envent1;
186           struct options_entry                    *o;
187           struct options_array_item     *a;
188           union options_value           *ov;
189           int                                      found;
190 
191           o = options_get(oo, "update-environment");
192           if (o == NULL)
193                     return;
194           a = options_array_first(o);
195           while (a != NULL) {
196                     ov = options_array_item_value(a);
197                     found = 0;
198                     RB_FOREACH_SAFE(envent, environ, src, envent1) {
199                               if (fnmatch(ov->string, envent->name, 0) == 0) {
200                                         environ_set(dst, envent->name, 0, "%s", envent->value);
201                                         found = 1;
202                               }
203                     }
204                     if (!found)
205                               environ_clear(dst, ov->string);
206                     a = options_array_next(a);
207           }
208 }
209 
210 /* Push environment into the real environment - use after fork(). */
211 void
environ_push(struct environ * env)212 environ_push(struct environ *env)
213 {
214           struct environ_entry          *envent;
215 
216           environ = xcalloc(1, sizeof *environ);
217           RB_FOREACH(envent, environ, env) {
218                     if (envent->value != NULL &&
219                         *envent->name != '\0' &&
220                         (~envent->flags & ENVIRON_HIDDEN))
221                               setenv(envent->name, envent->value, 1);
222           }
223 }
224 
225 /* Log the environment. */
226 void
environ_log(struct environ * env,const char * fmt,...)227 environ_log(struct environ *env, const char *fmt, ...)
228 {
229           struct environ_entry          *envent;
230           va_list                        ap;
231           char                          *prefix;
232 
233           va_start(ap, fmt);
234           vasprintf(&prefix, fmt, ap);
235           va_end(ap);
236 
237           RB_FOREACH(envent, environ, env) {
238                     if (envent->value != NULL && *envent->name != '\0') {
239                               log_debug("%s%s=%s", prefix, envent->name,
240                                   envent->value);
241                     }
242           }
243 
244           free(prefix);
245 }
246 
247 /* Create initial environment for new child. */
248 struct environ *
environ_for_session(struct session * s,int no_TERM)249 environ_for_session(struct session *s, int no_TERM)
250 {
251           struct environ      *env;
252           const char          *value;
253           int                  idx;
254 
255           env = environ_create();
256           environ_copy(global_environ, env);
257           if (s != NULL)
258                     environ_copy(s->environ, env);
259 
260           if (!no_TERM) {
261                     value = options_get_string(global_options, "default-terminal");
262                     environ_set(env, "TERM", 0, "%s", value);
263                     environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
264                     environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
265           }
266 
267 #ifdef HAVE_SYSTEMD
268           environ_clear(env, "LISTEN_PID");
269           environ_clear(env, "LISTEN_FDS");
270           environ_clear(env, "LISTEN_FDNAMES");
271 #endif
272 
273           if (s != NULL)
274                     idx = s->id;
275           else
276                     idx = -1;
277           environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(),
278               idx);
279 
280           return (env);
281 }
282