1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2009 Nicholas Marriott <[email protected]>
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 <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#include "tmux.h"
26
27/*
28 * Environment - manipulate a set of environment variables.
29 */
30
31RB_HEAD(environ, environ_entry);
32static int environ_cmp(struct environ_entry *, struct environ_entry *);
33RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp);
34
35static int
36environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
37{
38 return (strcmp(envent1->name, envent2->name));
39}
40
41/* Initialise the environment. */
42struct environ *
43environ_create(void)
44{
45 struct environ *env;
46
47 env = xcalloc(1, sizeof *env);
48 RB_INIT(env);
49
50 return (env);
51}
52
53/* Free an environment. */
54void
55environ_free(struct environ *env)
56{
57 struct environ_entry *envent, *envent1;
58
59 RB_FOREACH_SAFE(envent, environ, env, envent1) {
60 RB_REMOVE(environ, env, envent);
61 free(envent->name);
62 free(envent->value);
63 free(envent);
64 }
65 free(env);
66}
67
68struct environ_entry *
69environ_first(struct environ *env)
70{
71 return (RB_MIN(environ, env));
72}
73
74struct environ_entry *
75environ_next(struct environ_entry *envent)
76{
77 return (RB_NEXT(environ, env, envent));
78}
79
80/* Copy one environment into another. */
81void
82environ_copy(struct environ *srcenv, struct environ *dstenv)
83{
84 struct environ_entry *envent;
85
86 RB_FOREACH(envent, environ, srcenv) {
87 if (envent->value == NULL)
88 environ_clear(dstenv, envent->name);
89 else
90 environ_set(dstenv, envent->name, "%s", envent->value);
91 }
92}
93
94/* Find an environment variable. */
95struct environ_entry *
96environ_find(struct environ *env, const char *name)
97{
98 struct environ_entry envent;
99
100 envent.name = (char *) name;
101 return (RB_FIND(environ, env, &envent));
102}
103
104/* Set an environment variable. */
105void
106environ_set(struct environ *env, const char *name, const char *fmt, ...)
107{
108 struct environ_entry *envent;
109 va_list ap;
110
111 va_start(ap, fmt);
112 if ((envent = environ_find(env, name)) != NULL) {
113 free(envent->value);
114 xvasprintf(&envent->value, fmt, ap);
115 } else {
116 envent = xmalloc(sizeof *envent);
117 envent->name = xstrdup(name);
118 xvasprintf(&envent->value, fmt, ap);
119 RB_INSERT(environ, env, envent);
120 }
121 va_end(ap);
122}
123
124/* Clear an environment variable. */
125void
126environ_clear(struct environ *env, const char *name)
127{
128 struct environ_entry *envent;
129
130 if ((envent = environ_find(env, name)) != NULL) {
131 free(envent->value);
132 envent->value = NULL;
133 } else {
134 envent = xmalloc(sizeof *envent);
135 envent->name = xstrdup(name);
136 envent->value = NULL;
137 RB_INSERT(environ, env, envent);
138 }
139}
140
141/* Set an environment variable from a NAME=VALUE string. */
142void
143environ_put(struct environ *env, const char *var)
144{
145 char *name, *value;
146
147 value = strchr(var, '=');
148 if (value == NULL)
149 return;
150 value++;
151
152 name = xstrdup(var);
153 name[strcspn(name, "=")] = '\0';
154
155 environ_set(env, name, "%s", value);
156 free(name);
157}
158
159/* Unset an environment variable. */
160void
161environ_unset(struct environ *env, const char *name)
162{
163 struct environ_entry *envent;
164
165 if ((envent = environ_find(env, name)) == NULL)
166 return;
167 RB_REMOVE(environ, env, envent);
168 free(envent->name);
169 free(envent->value);
170 free(envent);
171}
172
173/* Copy variables from a destination into a source * environment. */
174void
175environ_update(struct options *oo, struct environ *src, struct environ *dst)
176{
177 struct environ_entry *envent;
178 struct options_entry *o;
179 struct options_array_item *a;
180 union options_value *ov;
181
182 o = options_get(oo, "update-environment");
183 if (o == NULL)
184 return;
185 a = options_array_first(o);
186 while (a != NULL) {
187 ov = options_array_item_value(a);
188 if ((envent = environ_find(src, ov->string)) == NULL)
189 environ_clear(dst, ov->string);
190 else
191 environ_set(dst, envent->name, "%s", envent->value);
192 a = options_array_next(a);
193 }
194}
195
196/* Push environment into the real environment - use after fork(). */
197void
198environ_push(struct environ *env)
199{
200 struct environ_entry *envent;
201
202 environ = xcalloc(1, sizeof *environ);
203 RB_FOREACH(envent, environ, env) {
204 if (envent->value != NULL && *envent->name != '\0')
205 setenv(envent->name, envent->value, 1);
206 }
207}
208
209/* Log the environment. */
210void
211environ_log(struct environ *env, const char *fmt, ...)
212{
213 struct environ_entry *envent;
214 va_list ap;
215 char *prefix;
216
217 va_start(ap, fmt);
218 vasprintf(&prefix, fmt, ap);
219 va_end(ap);
220
221 RB_FOREACH(envent, environ, env) {
222 if (envent->value != NULL && *envent->name != '\0') {
223 log_debug("%s%s=%s", prefix, envent->name,
224 envent->value);
225 }
226 }
227
228 free(prefix);
229}
230
231/* Create initial environment for new child. */
232struct environ *
233environ_for_session(struct session *s, int no_TERM)
234{
235 struct environ *env;
236 const char *value;
237 int idx;
238
239 env = environ_create();
240 environ_copy(global_environ, env);
241 if (s != NULL)
242 environ_copy(s->environ, env);
243
244 if (!no_TERM) {
245 value = options_get_string(global_options, "default-terminal");
246 environ_set(env, "TERM", "%s", value);
247 }
248
249 if (s != NULL)
250 idx = s->id;
251 else
252 idx = -1;
253 environ_set(env, "TMUX", "%s,%ld,%d", socket_path, (long)getpid(), idx);
254
255 return (env);
256}
257