1 /* direvent - directory content watcher daemon
2 Copyright (C) 2012-2016 Sergey Poznyakoff
3
4 Direvent is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Direvent is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with direvent. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "direvent.h"
18 #include "wordsplit.h"
19 #include <ctype.h>
20
21 extern char **environ; /* Environment */
22
23 #define DEBUG_ENVIRON(l,env) do { \
24 if (debug_level >= (l)) { \
25 diag(LOG_DEBUG, _("environment: ")); \
26 for (i = 0; (env)[i]; i++) \
27 diag(LOG_DEBUG, "%s ", (env)[i]); \
28 diag(LOG_DEBUG, "\n"); \
29 } \
30 } while (0)
31
32 static int
find_env_pos(char ** env,char * name,size_t * idx,size_t * valoff)33 find_env_pos(char **env, char *name, size_t *idx, size_t *valoff)
34 {
35 size_t nlen = strcspn(name, "+=");
36 size_t i;
37
38 for (i = 0; env[i]; i++) {
39 size_t elen = strcspn(env[i], "=");
40 if (elen == nlen && memcmp(name, env[i], nlen) == 0) {
41 if (idx)
42 *idx = i;
43 if (valoff)
44 *valoff = elen + 1;
45 return 0;
46 }
47 }
48 return -1;
49 }
50
51 static char *
find_env_ptr(char ** env,char * name,int val)52 find_env_ptr(char **env, char *name, int val)
53 {
54 size_t i, j;
55 if (find_env_pos(env, name, &i, &j))
56 return NULL;
57 return val ? env[i] + j : env[i];
58 }
59
60 static int
var_is_unset(char ** env,const char * name)61 var_is_unset(char **env, const char *name)
62 {
63 int i;
64 int nlen = strcspn(name, "=");
65
66 for (i = 0; env[i]; i++) {
67 if (env[i][0] == '-') {
68 size_t elen = strcspn(env[i] + 1, "=");
69 if (elen == nlen &&
70 memcmp(name, env[i] + 1, nlen) == 0) {
71 if (env[i][nlen + 1])
72 return strcmp(name + nlen,
73 env[i] + 1 + nlen) == 0;
74 else
75 return 1;
76 }
77 }
78 }
79 return 0;
80 }
81
82 static char *
env_concat(const char * name,size_t namelen,const char * a,const char * b)83 env_concat(const char *name, size_t namelen, const char *a, const char *b)
84 {
85 char *res;
86 size_t len;
87
88 if (a && b) {
89 res = emalloc(namelen + 1 + strlen(a) + strlen(b) + 1);
90 strcpy(res + namelen + 1, a);
91 strcat(res + namelen + 1, b);
92 } else if (a) {
93 len = strlen(a);
94 if (ispunct(a[len-1]))
95 len--;
96 res = emalloc(namelen + 1 + len + 1);
97 memcpy(res + namelen + 1, a, len);
98 res[namelen + 1 + len] = 0;
99 }
100 else { /* if (a == NULL) */
101 if (ispunct(b[0]))
102 b++;
103 len = strlen(b);
104 res = emalloc(namelen + 1 + len + 1);
105 strcpy(res + namelen + 1, b);
106 }
107 memcpy(res, name, namelen);
108 res[namelen] = '=';
109 return res;
110 }
111
112 static char *defenv[] = {
113 "DIREVENT_SYSEV_CODE=${sysev_code}",
114 "DIREVENT_SYSEV_NAME=${sysev_name}",
115 "DIREVENT_GENEV_CODE=${genev_code}",
116 "DIREVENT_GENEV_NAME=${genev_name}",
117 "DIREVENT_FILE=${file}",
118 NULL
119 };
120
121 char **
environ_setup(char ** hint,char ** kve)122 environ_setup(char **hint, char **kve)
123 {
124 char *empty[1] = { NULL };
125 char **old_env = environ;
126 char **new_env;
127 char **addenv = defenv;
128 char *var;
129 size_t count, i, j, n;
130 struct wordsplit ws;
131 int wsflags = WRDSF_NOCMD | WRDSF_QUOTE | WRDSF_NOSPLIT |
132 WRDSF_ENV | WRDSF_ENV_KV;
133
134 ws.ws_env = (const char **) kve;
135
136 if (!hint)
137 hint = empty;
138 else if (strcmp(hint[0], "-") == 0 || strcmp(hint[0], "--") == 0) {
139 old_env = NULL;
140 if (hint[0][1] == '-')
141 addenv = empty;
142 hint++;
143 }
144
145 /* Count new environment size */
146 count = 0;
147 if (old_env)
148 for (i = 0; old_env[i]; i++)
149 count++;
150
151 for (i = 0; addenv[i]; i++)
152 count++;
153
154 for (i = 0; hint[i]; i++)
155 count++;
156
157 if (self_test_pid)
158 count++;
159
160 /* Allocate new environment. */
161 new_env = ecalloc(count + 1, sizeof new_env[0]);
162
163 /* Populate the environment. */
164 n = 0;
165
166 if (old_env)
167 for (i = 0; old_env[i]; i++) {
168 if (!var_is_unset(hint, old_env[i]))
169 new_env[n++] = old_env[i];
170 }
171
172 for (i = 0; addenv[i]; i++)
173 if (!var_is_unset(hint, addenv[i])) {
174 if (wordsplit(addenv[i], &ws, wsflags)) {
175 diag(LOG_CRIT, "wordsplit: %s",
176 wordsplit_strerror(&ws));
177 _exit(127);
178 }
179 wsflags |= WRDSF_REUSE;
180 new_env[n++] = estrdup(ws.ws_wordv[0]);
181 }
182
183 for (i = 0; hint[i]; i++) {
184 char *p;
185
186 if (hint[i][0] == '-') {
187 /* Skip unset directives. */
188 continue;
189 }
190
191 if (wordsplit(hint[i], &ws, wsflags)) {
192 diag(LOG_CRIT, "wordsplit: %s",
193 wordsplit_strerror(&ws));
194 _exit(127);
195 }
196 wsflags |= WRDSF_REUSE;
197 var = ws.ws_wordv[0];
198
199 /* Find the slot for the variable. Use next available
200 slot if there's no such variable in new_env */
201 if (find_env_pos(new_env, hint[i], &j, NULL))
202 j = n;
203
204 if ((p = strchr(var, '='))) {
205 if (p == var)
206 continue; /* Ignore erroneous entry */
207
208 if (p[-1] == '+')
209 new_env[j] = env_concat(var,
210 p - var - 1,
211 find_env_ptr(environ,
212 var, 1),
213 p + 1);
214 else if (p[1] == '+')
215 new_env[j] = env_concat(var,
216 p - var,
217 p + 2,
218 find_env_ptr(environ,
219 var, 1));
220 else
221 new_env[j] = estrdup(var);
222 } else if ((p = find_env_ptr(environ, hint[i], 0)))
223 new_env[j] = p;
224 else
225 continue;
226 /* Adjust environment size */
227 if (j == n)
228 ++n;
229 }
230 if (self_test_pid) {
231 char buf[512];
232 snprintf(buf, sizeof buf, "DIREVENT_SELF_TEST_PID=%lu",
233 (unsigned long)self_test_pid);
234 new_env[n++] = estrdup(buf);;
235 }
236 new_env[n] = NULL;
237
238 if (wsflags & WRDSF_REUSE)
239 wordsplit_free(&ws);
240 return new_env;
241 }
242