File: bash.c

package info (click to toggle)
swish-e 2.4.7-1
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 7,224 kB
  • ctags: 8,194
  • sloc: ansic: 51,637; sh: 8,895; perl: 3,018; makefile: 591; xml: 9
file content (304 lines) | stat: -rw-r--r-- 8,621 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/*
 * Copyright (C) 1987 - 1999 Free Software Foundation, Inc.
 *
 * This file is based on stuff from GNU Bash 1.14.7, the Bourne Again SHell.
 * Everything that was changed is marked with the word `CHANGED'.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
/*
 Mon May  9 15:57:53 CDT 2005

did not update license info here because
 (a) the original is still ok per GPL
 (b) the original is lifted from another package and it's better to preserve
 (c) this file is only used in swish-e binary, not libswish-e
 
*/


#include "sys.h"
#include "mem.h"  /* swish-e memory */
#include <sys/stat.h>
#include "bash.h"

/* bash uses GID_T, autoconf defines GETGROUPS_T */
#define GID_T GETGROUPS_T

/*
 * CHANGED:
 * Perhaps these need new configure.in entries.
 * The following macro's are used in bash, and below:
 */
#undef SHELL
#undef AFS
#undef NOGROUP

/*
 * CHANGED:
 * - Added prototypes,
 * - used ANSI function arguments,
 * - made all functions static and
 * - changed all occurences of 'char *' into 'const char *' where possible.
 * - exported functions needed in which.c
 */
static int group_member (GID_T gid);
static char *extract_colon_unit (const char *string, int *p_index);

/*===========================================================================
 *
 * Almost everything below is from execute_cmd.c from bash-1.14.7,
 * a few functions are from other files: test.c, general.c and variables.c.
 *
 */

#if defined (HAVE_GETGROUPS)
/* The number of groups that this user is a member of. */
static int ngroups = 0;
static GID_T *group_array = (GID_T *)NULL;
static int default_group_array_size = 0;
#endif /* HAVE_GETGROUPS */

#if !defined (NOGROUP)
#  define NOGROUP (GID_T) -1
#endif

/* Return non-zero if GID is one that we have in our groups list. */
static int
group_member (GID_T gid)
{
  static GID_T pgid = (GID_T)NOGROUP;
  static GID_T egid = (GID_T)NOGROUP;

  if (pgid == (GID_T)NOGROUP)
#if defined (SHELL)
    pgid = (GID_T) current_user.gid;
#else /* !SHELL */
    pgid = (GID_T) getgid ();
#endif /* !SHELL */

  if (egid == (GID_T)NOGROUP)
#if defined (SHELL)
    egid = (GID_T) current_user.egid;
#else /* !SHELL */
    egid = (GID_T) getegid ();
#endif /* !SHELL */

  if (gid == pgid || gid == egid)
    return (1);

#if defined (HAVE_GETGROUPS)
  /* getgroups () returns the number of elements that it was able to
     place into the array.  We simply continue to call getgroups ()
     until the number of elements placed into the array is smaller than
     the physical size of the array. */

  while (ngroups == default_group_array_size)
    {
      default_group_array_size += 64;

      group_array = (GID_T *)
        xrealloc (group_array, default_group_array_size * sizeof (GID_T));

      ngroups = getgroups (default_group_array_size, group_array);
    }

  /* In case of error, the user loses. */
  if (ngroups < 0)
    return (0);

  /* Search through the list looking for GID. */
  {
    register int i;

    for (i = 0; i < ngroups; i++)
      if (gid == group_array[i])
        return (1);
  }
#endif /* HAVE_GETGROUPS */

  return (0);
}

#define u_mode_bits(x) (((x) & 0000700) >> 6)
#define g_mode_bits(x) (((x) & 0000070) >> 3)
#define o_mode_bits(x) (((x) & 0000007) >> 0)
#define X_BIT(x) ((x) & 1)

/* Return some flags based on information about this file.
   The EXISTS bit is non-zero if the file is found.
   The EXECABLE bit is non-zero the file is executble.
   Zero is returned if the file is not found. */
int
file_status (const char *name)
{
  struct stat finfo;
  static int user_id = -1;

  /* Determine whether this file exists or not. */
  if (stat (name, &finfo) < 0)
    return (0);

  /* If the file is a directory, then it is not "executable" in the
     sense of the shell. */
  if (S_ISDIR (finfo.st_mode))
    return (FS_EXISTS);

#if defined (AFS)
  /* We have to use access(2) to determine access because AFS does not
     support Unix file system semantics.  This may produce wrong
     answers for non-AFS files when ruid != euid.  I hate AFS. */
  if (access (name, X_OK) == 0)
    return (FS_EXISTS | FS_EXECABLE);
  else
    return (FS_EXISTS);
#else /* !AFS */

  /* Find out if the file is actually executable.  By definition, the
     only other criteria is that the file has an execute bit set that
     we can use. */
  if (user_id == -1)
    user_id = geteuid (); /* CHANGED: bash uses: current_user.euid; */

  /* Root only requires execute permission for any of owner, group or
     others to be able to exec a file. */
  if (user_id == 0)
    {
      int bits;

      bits = (u_mode_bits (finfo.st_mode) |
              g_mode_bits (finfo.st_mode) |
              o_mode_bits (finfo.st_mode));

      if (X_BIT (bits))
        return (FS_EXISTS | FS_EXECABLE);
    }

  /* If we are the owner of the file, the owner execute bit applies. */
  if (user_id == finfo.st_uid )
      return  X_BIT (u_mode_bits (finfo.st_mode)) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS;

  /* If we are in the owning group, the group permissions apply. */
  if (group_member (finfo.st_gid) )
      return  X_BIT (g_mode_bits (finfo.st_mode)) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS;

  /* If `others' have execute permission to the file, then so do we, since we are also `others'. */
  return X_BIT (o_mode_bits (finfo.st_mode)) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS;

#endif /* !AFS */
}

/* Return 1 if STRING is an absolute program name; it is absolute if it
   contains any slashes.  This is used to decide whether or not to look
   up through $PATH. */
int
absolute_program (const char *string)
{
  return ((char *)strchr (string, '/') != (char *)NULL);
}

/* Given a string containing units of information separated by colons,
   return the next one pointed to by (P_INDEX), or NULL if there are no more.
   Advance (P_INDEX) to the character after the colon. */
char *
extract_colon_unit (const char *string, int *p_index)
{
  int i, start;
  char path_separator;

#if defined( PATH_SEPARATOR )
  path_separator = PATH_SEPARATOR[0];
#else
  path_separator = ':';
#endif

  i = *p_index;

  if (!string || (i >= (int)strlen (string)))
    return ((char *)NULL);

  /* Each call to this routine leaves the index pointing at a colon if
     there is more to the path.  If I is > 0, then increment past the
     `:'.  If I is 0, then the path has a leading colon.  Trailing colons
     are handled OK by the `else' part of the if statement; an empty
     string is returned in that case. */
  if (i && string[i] == path_separator )
    i++;

  start = i;

  while (string[i] && string[i] != path_separator ) i++;

  *p_index = i;

  if (i == start)
    {
      if (string[i])
        (*p_index)++;

      /* Return "" in the case of a trailing `:'. */
      return (savestring (""));
    }
  else
    {
      char *value;

      value = xmalloc (1 + i - start);
      strncpy (value, string + start, i - start);
      value [i - start] = '\0';

      return (value);
    }
}

/* Return the next element from PATH_LIST, a colon separated list of
   paths.  PATH_INDEX_POINTER is the address of an index into PATH_LIST;
   the index is modified by this function.
   Return the next element of PATH_LIST or NULL if there are no more. */
char *
get_next_path_element (const char *path_list, int *path_index_pointer)
{
  char *path;

  path = extract_colon_unit (path_list, path_index_pointer);

  if (!path)
    return (path);

  if (!*path)
    {
      xfree (path);
      path = savestring (".");
    }

  return (path);
}

/* Turn PATH, a directory, and NAME, a filename, into a full pathname.
   This allocates new memory and returns it. */
char *
make_full_pathname (const char *path, const char *name, int name_len)
{
  char *full_path;
  int path_len;

  path_len = strlen (path);
  full_path = (char *) xmalloc (2 + path_len + name_len);
  strcpy (full_path, path);
  full_path[path_len] = '/';
  strcpy (full_path + path_len + 1, name);
  return (full_path);
}