1 | /* Copyright 1998 by the Massachusetts Institute of Technology. |
---|
2 | * |
---|
3 | * Permission to use, copy, modify, and distribute this |
---|
4 | * software and its documentation for any purpose and without |
---|
5 | * fee is hereby granted, provided that the above copyright |
---|
6 | * notice appear in all copies and that both that copyright |
---|
7 | * notice and this permission notice appear in supporting |
---|
8 | * documentation, and that the name of M.I.T. not be used in |
---|
9 | * advertising or publicity pertaining to distribution of the |
---|
10 | * software without specific, written prior permission. |
---|
11 | * M.I.T. makes no representations about the suitability of |
---|
12 | * this software for any purpose. It is provided "as is" |
---|
13 | * without express or implied warranty. |
---|
14 | */ |
---|
15 | |
---|
16 | /* This is the part of attach that is used by the "add" alias. */ |
---|
17 | |
---|
18 | static const char rcsid[] = "$Id: add.c,v 1.15 2005-09-15 14:22:00 rbasch Exp $"; |
---|
19 | |
---|
20 | #include <sys/types.h> |
---|
21 | #include <sys/stat.h> |
---|
22 | #include <errno.h> |
---|
23 | #include <stdio.h> |
---|
24 | #include <stdlib.h> |
---|
25 | #include <string.h> |
---|
26 | #include <unistd.h> |
---|
27 | |
---|
28 | #include <athdir.h> |
---|
29 | #include <locker.h> |
---|
30 | |
---|
31 | #include "agetopt.h" |
---|
32 | #include "attach.h" |
---|
33 | |
---|
34 | static void usage(void); |
---|
35 | static void modify_path(char **path, char *elt); |
---|
36 | static void print_readable_path(char *path); |
---|
37 | static int add_callback(locker_context context, locker_attachent *at, |
---|
38 | void *arg); |
---|
39 | static int ruid_stat(const char *path, struct stat *st); |
---|
40 | |
---|
41 | static struct agetopt_option add_options[] = { |
---|
42 | { "verbose", 'v', 0 }, |
---|
43 | { "quiet", 'q', 0 }, |
---|
44 | { "debug", 'd', 0 }, |
---|
45 | { "front", 'f', 0 }, |
---|
46 | { "remove", 'r', 0 }, |
---|
47 | { "print", 'p', 0 }, |
---|
48 | { "warn", 'w', 0 }, |
---|
49 | { "bourne", 'b', 0 }, |
---|
50 | { "path", 'P', 1 }, |
---|
51 | { "attachopts", 'a', 0 } |
---|
52 | }; |
---|
53 | |
---|
54 | static char *shell_templates[2][2] = |
---|
55 | { |
---|
56 | { |
---|
57 | "setenv PATH %s; setenv MANPATH %s; setenv INFOPATH %s\n", |
---|
58 | "PATH=%s; export PATH; MANPATH=%s; export MANPATH; INFOPATH=%s; export INFOPATH\n" |
---|
59 | }, |
---|
60 | { |
---|
61 | "set athena_path=(%s); setenv MANPATH %s; setenv INFOPATH %s\n", |
---|
62 | "athena_path=%s; MANPATH=%s; export MANPATH; INFOPATH=%s; export INFOPATH\n" |
---|
63 | } |
---|
64 | }; |
---|
65 | |
---|
66 | extern locker_callback attach_callback; |
---|
67 | |
---|
68 | static int quiet = 0, give_warnings = 0, remove_from_path = 0; |
---|
69 | static int add_to_front = 0, bourne_shell = 0, use_athena_path = 0; |
---|
70 | static char *path, *manpath, *infopath; |
---|
71 | |
---|
72 | int add_main(int argc, char **argv) |
---|
73 | { |
---|
74 | int print_path, end_args; |
---|
75 | int opt; |
---|
76 | |
---|
77 | print_path = end_args = 0; |
---|
78 | |
---|
79 | while (!end_args && (opt = attach_getopt(argc, argv, add_options)) != -1) |
---|
80 | { |
---|
81 | switch (opt) |
---|
82 | { |
---|
83 | case 'q': |
---|
84 | quiet = 1; |
---|
85 | break; |
---|
86 | |
---|
87 | case 'f': |
---|
88 | add_to_front = 1; |
---|
89 | break; |
---|
90 | |
---|
91 | case 'r': |
---|
92 | remove_from_path = 1; |
---|
93 | break; |
---|
94 | |
---|
95 | case 'p': |
---|
96 | print_path = 1; |
---|
97 | break; |
---|
98 | |
---|
99 | case 'w': |
---|
100 | give_warnings = 1; |
---|
101 | break; |
---|
102 | |
---|
103 | case 'b': |
---|
104 | bourne_shell = 1; |
---|
105 | break; |
---|
106 | |
---|
107 | case 'P': |
---|
108 | use_athena_path = 1; |
---|
109 | path = optarg; |
---|
110 | break; |
---|
111 | |
---|
112 | case 'a': |
---|
113 | if (remove_from_path || print_path) |
---|
114 | { |
---|
115 | fprintf(stderr, "%s: can't use -a with -r or -p.\n", whoami); |
---|
116 | usage(); |
---|
117 | } |
---|
118 | end_args = 1; |
---|
119 | break; |
---|
120 | |
---|
121 | case 'd': |
---|
122 | case 'v': |
---|
123 | fprintf(stderr, "%s: The '%c' flag is no longer supported.\n", |
---|
124 | whoami, opt); |
---|
125 | break; |
---|
126 | |
---|
127 | case '?': |
---|
128 | usage(); |
---|
129 | } |
---|
130 | } |
---|
131 | |
---|
132 | if (!path) |
---|
133 | path = getenv("PATH"); |
---|
134 | if (!path) |
---|
135 | path = ""; |
---|
136 | path = strdup(path); |
---|
137 | if (!path) |
---|
138 | { |
---|
139 | fprintf(stderr, "%s: Out of memory.\n", whoami); |
---|
140 | exit(1); |
---|
141 | } |
---|
142 | if (use_athena_path && !bourne_shell) |
---|
143 | { |
---|
144 | char *p; |
---|
145 | for (p = path; *p; p++) |
---|
146 | { |
---|
147 | if (*p == ' ') |
---|
148 | *p = ':'; |
---|
149 | } |
---|
150 | } |
---|
151 | manpath = getenv("MANPATH"); |
---|
152 | if (manpath) |
---|
153 | manpath = strdup(manpath); |
---|
154 | else |
---|
155 | manpath = strdup(":"); |
---|
156 | |
---|
157 | infopath = getenv("INFOPATH"); |
---|
158 | if (infopath) |
---|
159 | infopath = strdup(infopath); |
---|
160 | else |
---|
161 | infopath = strdup(""); |
---|
162 | |
---|
163 | /* If no arguments have been directed to attach, or -p was |
---|
164 | * specified, we output the path in an easier-to-read format and |
---|
165 | * we're done. |
---|
166 | */ |
---|
167 | if (argc == optind || print_path) |
---|
168 | { |
---|
169 | print_readable_path(path); |
---|
170 | exit(0); |
---|
171 | } |
---|
172 | |
---|
173 | /* If the following args weren't explicitly handed to attach (via |
---|
174 | * -a), and the first one looks like a pathname, then assume we've |
---|
175 | * been passed a bunch of pathnames rather than lockernames. |
---|
176 | */ |
---|
177 | if (!end_args && (argv[optind][0] == '.' || argv[optind][0] == '/')) |
---|
178 | { |
---|
179 | struct stat st; |
---|
180 | |
---|
181 | for (; optind < argc; optind++) |
---|
182 | { |
---|
183 | if (argv[optind][0] != '.' && argv[optind][0] != '/') |
---|
184 | { |
---|
185 | fprintf(stderr, "%s: only pathnames may be specified when " |
---|
186 | "pathnames are being added\n", whoami); |
---|
187 | usage(); |
---|
188 | } |
---|
189 | |
---|
190 | /* Make sure the directory exists, if we're adding it to the |
---|
191 | * path. Otherwise we don't care. |
---|
192 | */ |
---|
193 | if (!remove_from_path && ruid_stat(argv[optind], &st) == -1) |
---|
194 | { |
---|
195 | fprintf(stderr, "%s: no such path: %s\n", whoami, |
---|
196 | argv[optind]); |
---|
197 | } |
---|
198 | else |
---|
199 | modify_path(&path, argv[optind]); |
---|
200 | } |
---|
201 | } |
---|
202 | else if (remove_from_path) |
---|
203 | { |
---|
204 | locker_context context; |
---|
205 | int status; |
---|
206 | |
---|
207 | if (locker_init(&context, getuid(), NULL, NULL)) |
---|
208 | exit(1); |
---|
209 | |
---|
210 | for (; optind < argc; optind++) |
---|
211 | { |
---|
212 | locker_attachent *at; |
---|
213 | |
---|
214 | /* Ignore flags, just look at lockers */ |
---|
215 | if (argv[optind][0] == '-') |
---|
216 | continue; |
---|
217 | |
---|
218 | status = locker_read_attachent(context, argv[optind], &at); |
---|
219 | if (status != LOCKER_SUCCESS) |
---|
220 | continue; |
---|
221 | add_callback(context, at, NULL); |
---|
222 | locker_free_attachent(context, at); |
---|
223 | } |
---|
224 | |
---|
225 | locker_end(context); |
---|
226 | } |
---|
227 | else |
---|
228 | { |
---|
229 | /* We are adding lockers. */ |
---|
230 | |
---|
231 | attach_callback = add_callback; |
---|
232 | |
---|
233 | /* Reinvoke attach's main: optind now points to either the first |
---|
234 | * attach command-line argument or the first locker. Either way, |
---|
235 | * let attach deal (using our callback), and return to us when |
---|
236 | * it's done. |
---|
237 | */ |
---|
238 | attach_main(argc, argv); |
---|
239 | } |
---|
240 | |
---|
241 | if (use_athena_path && !bourne_shell) |
---|
242 | { |
---|
243 | char *p; |
---|
244 | for (p = path; *p; p++) |
---|
245 | { |
---|
246 | if (*p == ':') |
---|
247 | *p = ' '; |
---|
248 | } |
---|
249 | } |
---|
250 | |
---|
251 | printf(shell_templates[use_athena_path][bourne_shell], |
---|
252 | path, manpath, infopath); |
---|
253 | free(path); |
---|
254 | free(manpath); |
---|
255 | free(infopath); |
---|
256 | exit(0); |
---|
257 | } |
---|
258 | |
---|
259 | static int add_callback(locker_context context, locker_attachent *at, |
---|
260 | void *arg) |
---|
261 | { |
---|
262 | char **found, **ptr; |
---|
263 | |
---|
264 | /* Find the binary directories we want to add to/remove from the path. */ |
---|
265 | found = athdir_get_paths(at->mountpoint, "bin", NULL, NULL, NULL, NULL, 0); |
---|
266 | if (found) |
---|
267 | { |
---|
268 | for (ptr = found; *ptr; ptr++) |
---|
269 | { |
---|
270 | if (!remove_from_path && !athdir_native(*ptr, NULL) && give_warnings) |
---|
271 | { |
---|
272 | fprintf(stderr, "%s: warning: using compatibility for %s\n", |
---|
273 | whoami, at->mountpoint); |
---|
274 | } |
---|
275 | modify_path(&path, *ptr); |
---|
276 | } |
---|
277 | athdir_free_paths(found); |
---|
278 | } |
---|
279 | else |
---|
280 | { |
---|
281 | if (give_warnings) |
---|
282 | { |
---|
283 | fprintf(stderr, "%s: warning: %s has no binary directory\n", |
---|
284 | whoami, at->mountpoint); |
---|
285 | } |
---|
286 | } |
---|
287 | |
---|
288 | /* Find the man directories we want to add to/remove from the manpath. */ |
---|
289 | found = athdir_get_paths(at->mountpoint, "man", NULL, NULL, NULL, NULL, 0); |
---|
290 | if (found) |
---|
291 | { |
---|
292 | for (ptr = found; *ptr; ptr++) |
---|
293 | modify_path(&manpath, *ptr); |
---|
294 | athdir_free_paths(found); |
---|
295 | } |
---|
296 | |
---|
297 | /* Find the info directories we want to add to/remove from the infopath. */ |
---|
298 | found = athdir_get_paths(at->mountpoint, "info", NULL, NULL, NULL, NULL, 0); |
---|
299 | if (found) |
---|
300 | { |
---|
301 | for (ptr = found; *ptr; ptr++) |
---|
302 | modify_path(&infopath, *ptr); |
---|
303 | athdir_free_paths(found); |
---|
304 | } |
---|
305 | |
---|
306 | return 0; |
---|
307 | } |
---|
308 | |
---|
309 | static void modify_path(char **pathp, char *elt) |
---|
310 | { |
---|
311 | char *p; |
---|
312 | int len = strlen(elt); |
---|
313 | |
---|
314 | /* If we're adding a string to the front of the path, we need |
---|
315 | * to remove it from the middle first, if it's already there. |
---|
316 | */ |
---|
317 | if (remove_from_path || add_to_front) |
---|
318 | { |
---|
319 | p = *pathp; |
---|
320 | while (p) |
---|
321 | { |
---|
322 | if (!strncmp(p, elt, len) && (p[len] == ':' || p[len] == '\0')) |
---|
323 | { |
---|
324 | if (p[len] == ':') |
---|
325 | len++; |
---|
326 | else if (p != *pathp) |
---|
327 | { |
---|
328 | p--; |
---|
329 | len++; |
---|
330 | } |
---|
331 | |
---|
332 | memmove(p, p + len, strlen(p + len) + 1); |
---|
333 | } |
---|
334 | |
---|
335 | p = strchr(p, ':'); |
---|
336 | if (p) |
---|
337 | p++; |
---|
338 | } |
---|
339 | } |
---|
340 | else |
---|
341 | { |
---|
342 | /* Adding to end, so make sure the path element isn't already in |
---|
343 | * the middle. |
---|
344 | */ |
---|
345 | if ((p = strstr(*pathp, elt)) && |
---|
346 | (p[len] == ':' || p[len] == '\0') && |
---|
347 | (p == *pathp || *(p - 1) == ':')) |
---|
348 | return; |
---|
349 | } |
---|
350 | |
---|
351 | |
---|
352 | if (!remove_from_path) |
---|
353 | { |
---|
354 | p = malloc(strlen(*pathp) + len + 2); |
---|
355 | if (!p) |
---|
356 | { |
---|
357 | fprintf(stderr, "%s: Out of memory.\n", whoami); |
---|
358 | exit(1); |
---|
359 | } |
---|
360 | if (add_to_front) |
---|
361 | sprintf(p, "%s%s%s", elt, **pathp ? ":" : "", *pathp); |
---|
362 | else |
---|
363 | sprintf(p, "%s%s%s", *pathp, **pathp ? ":" : "", elt); |
---|
364 | free(*pathp); |
---|
365 | *pathp = p; |
---|
366 | } |
---|
367 | } |
---|
368 | |
---|
369 | /* print_readable_path |
---|
370 | * |
---|
371 | * A hack to print out a readable version of the user's path. |
---|
372 | * |
---|
373 | * It's a hack because it's not currently a nice thing to do |
---|
374 | * correctly. So, if any path element starts with "/mit" and ends with |
---|
375 | * "bin" such as "/mit/gnu/arch/sun4x_55/bin," we print "{add gnu}" |
---|
376 | * instead. This is not always correct; it misses things that are not |
---|
377 | * mounted under /mit, and is misleading for lockers that do not mount |
---|
378 | * under /mit/lockername as well as MUL type filesystems. However, |
---|
379 | * these occasions are infrequent. |
---|
380 | * |
---|
381 | * In addition, each path starting with "/mit" and ending with "bin" |
---|
382 | * is tested for the substring of the machine's $ATHENA_SYS value. If |
---|
383 | * absent, it is assumed that some form of compatibility system is |
---|
384 | * being used, and a * is added to the shortened path string. So if |
---|
385 | * ATHENA_SYS_COMPAT is set to sun4x_55 while ATHENA_SYS is set to |
---|
386 | * sun4x_56, in the example above "{add gnu*}" would be printed |
---|
387 | * instead of "{add gnu}." |
---|
388 | * |
---|
389 | * XXX We could do a less hacky version of this using |
---|
390 | * locker_iterate_attachtab to get all of the mountpoints. It's not clear |
---|
391 | * that there's a lot of benefit to this though. */ |
---|
392 | static void print_readable_path(char *path) |
---|
393 | { |
---|
394 | char *p, *name, *name_end; |
---|
395 | |
---|
396 | for (p = strtok(path, ":"); p; p = strtok(NULL, ":")) |
---|
397 | { |
---|
398 | if (p != path) |
---|
399 | putc(bourne_shell ? ':' : ' ', stderr); |
---|
400 | |
---|
401 | if (!strncmp(p, "/mit/", 5)) |
---|
402 | { |
---|
403 | name = p + 5; |
---|
404 | name_end = strchr(name, '/'); |
---|
405 | if (name_end && !strcmp(p + strlen(p) - 3, "bin")) |
---|
406 | { |
---|
407 | if (athdir_native(name, NULL)) |
---|
408 | fprintf(stderr, "{add %.*s}", name_end - name, name); |
---|
409 | else |
---|
410 | fprintf(stderr, "{add %.*s*}", name_end - name, name); |
---|
411 | } |
---|
412 | else |
---|
413 | fprintf(stderr, "%s", p); |
---|
414 | } |
---|
415 | else |
---|
416 | fprintf(stderr, "%s", p); |
---|
417 | } |
---|
418 | |
---|
419 | fprintf(stderr, "\n"); |
---|
420 | } |
---|
421 | |
---|
422 | /* stat() the given path after setting the effective UID to the |
---|
423 | * real UID (if necessary), and return the value returned by stat(). |
---|
424 | */ |
---|
425 | static int ruid_stat(const char *path, struct stat *st) |
---|
426 | { |
---|
427 | uid_t euid = geteuid(); |
---|
428 | uid_t ruid = getuid(); |
---|
429 | int status; |
---|
430 | |
---|
431 | if (euid != ruid) |
---|
432 | seteuid(ruid); |
---|
433 | status = stat(path, st); |
---|
434 | if (euid != ruid) |
---|
435 | seteuid(euid); |
---|
436 | return status; |
---|
437 | } |
---|
438 | |
---|
439 | static void usage(void) |
---|
440 | { |
---|
441 | fprintf(stderr, "Usage: add [-vfrpwbq] [-P $athena_path] [-a attachflags] [lockername ...]\n"); |
---|
442 | fprintf(stderr, " add [-dfrb] [-P $athena_path] pathname ...\n"); |
---|
443 | exit(1); |
---|
444 | } |
---|