source: trunk/athena/bin/attach/add.c @ 22840

Revision 22840, 10.5 KB checked in by tabbott, 16 years ago (diff)
In attach: * Merged quilt patches into mainline Athena tree
Line 
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
18static 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
34static void usage(void);
35static void modify_path(char **path, char *elt);
36static void print_readable_path(char *path);
37static int add_callback(locker_context context, locker_attachent *at,
38                        void *arg);
39static int ruid_stat(const char *path, struct stat *st);
40
41static 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
54static 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
66extern locker_callback attach_callback;
67
68static int quiet = 0, give_warnings = 0, remove_from_path = 0;
69static int add_to_front = 0, bourne_shell = 0, use_athena_path = 0;
70static char *path, *manpath, *infopath;
71
72int 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
259static 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
309static 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.  */
392static 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 */
425static 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
439static 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}
Note: See TracBrowser for help on using the repository browser.