source: trunk/athena/bin/attachandrun/attachandrun.c @ 17545

Revision 17545, 6.3 KB checked in by ghudson, 22 years ago (diff)
Fix error messages not to have leading / in program name.
Line 
1/* Copyright 2000 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/* attachandrun - attach a locker and run a program out of it.
17 * Intended mainly for use by scripts.
18 */
19
20static const char rcsid[] = "$Id: attachandrun.c,v 1.3 2002-05-06 14:51:20 ghudson Exp $";
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <sys/wait.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <ctype.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <athdir.h>
33
34static char *run_attach(const char *locker);
35static char *read_from_pipe(int fd);
36static void try_shell_exec(char *path, int argc, char **argv);
37static void *emalloc(size_t size);
38static void *erealloc(void *ptr, size_t size);
39static void usage(void);
40
41static const char *progname;
42
43int main(int argc, char **argv)
44{
45  const char *locker, *program;
46  char *locker_path, **paths, *prog_path;
47  int check = 0;
48
49  progname = strrchr(argv[0], '/');
50  progname = (progname == NULL) ? argv[0] : progname + 1;
51
52  /* Process arguments.  Leave argc/argv specifying the arguments for
53   * the command to run.
54   */
55  if (argc >= 2 &&
56      (strcmp(argv[1], "--check") == 0 || strcmp(argv[1], "-c") == 0))
57    {
58      if (argc < 3)
59        usage();
60
61      check = 1;
62      argc--;
63      argv++;
64    }
65  else if (argc < 4)
66    usage();
67  locker = argv[1];
68  program = argv[2];
69  argc -= 3;
70  argv += 3;
71
72  /* Run attach and get the path to run. */
73  locker_path = run_attach(locker);
74  paths = athdir_get_paths(locker_path, "bin", NULL, NULL, NULL, NULL, 0);
75  if (paths == NULL)
76    {
77      fprintf(stderr, "%s: can't find a binary directory in %s\n",
78              progname, locker_path);
79      exit(1);
80    }
81  prog_path = emalloc(strlen(paths[0]) + 1 + strlen(program) + 1);
82  sprintf(prog_path, "%s/%s", paths[0], program);
83  athdir_free_paths(paths);
84
85  /* If we're just doing a check, do that now and exit. */
86  if (check)
87    exit((access(prog_path, X_OK) == 0) ? 0 : 1);
88
89  /* Run the command. */
90  execv(prog_path, argv);
91  if (errno == ENOEXEC)
92    try_shell_exec(prog_path, argc, argv);
93  fprintf(stderr, "%s: failure to exec %s: %s\n", progname, program,
94          strerror(errno));
95  exit(1);
96}
97
98/* Run attach -p <lockername> and return the locker path attach prints. */
99static char *run_attach(const char *locker)
100{
101  pid_t pid;
102  int fds[2], status;
103  char *locker_path;
104
105  if (pipe(fds) == -1)
106    {
107      fprintf(stderr, "%s: pipe failed: %s\n", progname, strerror(errno));
108      exit(1);
109    }
110
111  pid = fork();
112  if (pid == -1)
113    {
114      fprintf(stderr, "%s: fork failed: %s\n", progname, strerror(errno));
115      exit(1);
116    }
117  else if (pid == 0)
118    {
119      /* In the child, run attach with stdout directed at the write
120       * side of the pipe.
121       */
122      close(fds[0]);
123      dup2(fds[1], STDOUT_FILENO);
124      if (fds[1] != STDOUT_FILENO)
125        close(fds[1]);
126      execl("/bin/athena/attach", "attach", "-p", locker, (char *) NULL);
127      fprintf(stderr, "%s: execl failed: %s\n", progname, strerror(errno));
128      _exit(1);
129    }
130
131  /* In the parent, read the locker path from the pipe. */
132  close(fds[1]);
133  locker_path = read_from_pipe(fds[0]);
134  close(fds[0]);
135  waitpid(pid, &status, 0);
136  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
137    exit(1);
138  if (!*locker_path)
139    {
140      fprintf(stderr, "%s: attach failed to output locker path\n", progname);
141      exit(1);
142    }
143  return locker_path;
144}
145
146static char *read_from_pipe(int fd)
147{
148  char *buf;
149  int count, pos, sz;
150
151  sz = BUFSIZ;
152  buf = emalloc(sz + 1);
153  pos = 0;
154  while (1)
155    {
156      count = read(fd, buf + pos, sz - pos);
157      if (count == -1)
158        {
159          fprintf(stderr, "%s: read from pipe failed: %s\n", progname,
160                  strerror(errno));
161          exit(1);
162        }
163      else if (count == 0)
164        {
165          buf[pos] = 0;
166          if (pos > 0 && buf[pos - 1] == '\n')
167            buf[pos - 1] = 0;
168          return buf;
169        }
170      pos += count;
171      if (pos == sz)
172        {
173          sz += BUFSIZ;
174          buf = erealloc(buf, sz + 1);
175        }
176    }
177}
178
179/* Try to feed a text file to the shell by hand.  On error, errno will
180 * be the error value from open() or read() or execv(), or ENOEXEC if
181 * the file looks binary, or ENOMEM if we couldn't allocate memory for
182 * an argument list.
183 */
184static void try_shell_exec(char *path, int argc, char **argv)
185{
186  int i, count, fd, err;
187  unsigned char sample[128];
188  char **arg;
189
190  /* First we should check if the file looks binary.  Open the file. */
191  fd = open(path, O_RDONLY, 0);
192  if (fd == -1)
193    return;
194
195  /* Read in a bit of data from the file. */
196  count = read(fd, sample, sizeof(sample));
197  err = errno;
198  close(fd);
199  if (count < 0)
200    {
201      errno = err;
202      return;
203    }
204
205  /* Look for binary characters in the first line. */
206  for (i = 0; i < count; i++)
207    {
208      if (sample[i] == '\n')
209        break;
210      if (!isspace(sample[i]) && !isprint(sample[i]))
211        {
212          errno = ENOEXEC;
213          return;
214        }
215    }
216
217  /* Allocate space for a longer argument list. */
218  arg = malloc((argc + 2) * sizeof(char *));
219  if (!arg)
220    {
221      errno = ENOMEM;
222      return;
223    }
224
225  /* Set up the argument list. Copy in the argument part of argv
226   * including the terminating NULL. argv[0] is lost, unfortunately.
227   */
228  arg[0] = "/bin/sh";
229  arg[1] = path;
230  memcpy(arg + 2, argv + 1, argc * sizeof(char *));
231
232  execv(arg[0], arg);
233  free(arg);
234  return;
235}
236
237static void *emalloc(size_t size)
238{
239  void *ptr;
240
241  ptr = malloc(size);
242  if (!ptr)
243    {
244      fprintf(stderr, "%s: malloc size %lu failed", progname,
245              (unsigned long) size);
246      exit(1);
247    }
248  return ptr;
249}
250
251static void *erealloc(void *ptr, size_t size)
252{
253  ptr = realloc(ptr, size);
254  if (!ptr)
255    {
256      fprintf(stderr, "%s: realloc size %lu failed", progname,
257              (unsigned long) size);
258      exit(1);
259    }
260  return ptr;
261}
262
263static void usage(void)
264{
265  fprintf(stderr, "Usage: %s [--check] locker program argv0 [argv1...]\n",
266          progname);
267  exit(1);
268}
Note: See TracBrowser for help on using the repository browser.