aboutsummaryrefslogtreecommitdiff
path: root/newlib/libc/stdlib/system.c
blob: 34be6cd8f6689fbba69d820b3e7bbe1dfbe88ad6 (plain)
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
/*
FUNCTION
<<system>>---execute command string

INDEX
	system
INDEX
	_system_r

SYNOPSIS
	#include <stdlib.h>
	int system(char *<[s]>);

	int _system_r(void *<[reent]>, char *<[s]>);

DESCRIPTION

Use <<system>> to pass a command string <<*<[s]>>> to <</bin/sh>> on
your system, and wait for it to finish executing.

Use ``<<system(NULL)>>'' to test whether your system has <</bin/sh>>
available.

The alternate function <<_system_r>> is a reentrant version.  The
extra argument <[reent]> is a pointer to a reentrancy structure.

RETURNS
<<system(NULL)>> returns a non-zero value if <</bin/sh>> is available, and
<<0>> if it is not.

With a command argument, the result of <<system>> is the exit status
returned by <</bin/sh>>.

PORTABILITY
ANSI C requires <<system>>, but leaves the nature and effects of a
command processor undefined.  ANSI C does, however, specify that
<<system(NULL)>> return zero or nonzero to report on the existence of
a command processor.

POSIX.2 requires <<system>>, and requires that it invoke a <<sh>>.
Where <<sh>> is found is left unspecified.

Supporting OS subroutines required: <<_exit>>, <<_execve>>, <<_fork_r>>,
<<_wait_r>>.
*/

#include <_ansi.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <_syslist.h>
#include <reent.h>

#if defined (unix) || defined (__CYGWIN__)
static int _EXFUN(do_system, (struct _reent *ptr, const char *s));
#endif

int
_system_r (struct _reent *ptr,
     const char *s)
{
#if defined(HAVE_SYSTEM)
  return _system (s);
  ptr = ptr;
#elif defined(NO_EXEC)
  if (s == NULL)
    return 0;
  errno = ENOSYS;
  return -1;
#else

  /* ??? How to handle (s == NULL) here is not exactly clear.
     If _fork_r fails, that's not really a justification for returning 0.
     For now we always return 0 and leave it to each target to explicitly
     handle otherwise (this can always be relaxed in the future).  */

#if defined (unix) || defined (__CYGWIN__)
  if (s == NULL)
    return 1;
  return do_system (ptr, s);
#else
  if (s == NULL)
    return 0;
  errno = ENOSYS;
  return -1;
#endif

#endif
}

#ifndef _REENT_ONLY

int
system (const char *s)
{
  return _system_r (_REENT, s);
}

#endif

#if defined (unix) && !defined (__CYGWIN__) && !defined(__rtems__)
extern char **environ;

/* Only deal with a pointer to environ, to work around subtle bugs with shared
   libraries and/or small data systems where the user declares his own
   'environ'.  */
static char ***p_environ = &environ;

static int
do_system (struct _reent *ptr,
     const char *s)
{
  char *argv[4];
  int pid, status;

  argv[0] = "sh";
  argv[1] = "-c";
  argv[2] = (char *) s;
  argv[3] = NULL;

  if ((pid = _fork_r (ptr)) == 0)
    {
      _execve ("/bin/sh", argv, *p_environ);
      exit (100);
    }
  else if (pid == -1)
    return -1;
  else
    {
      int rc = _wait_r (ptr, &status);
      if (rc == -1)
	return -1;
      status = (status >> 8) & 0xff;
      return status;
    }
}
#endif

#if defined (__CYGWIN__)
static int
do_system (struct _reent *ptr,
     const char *s)
{
  char *argv[4];
  int pid, status;

  argv[0] = "sh";
  argv[1] = "-c";
  argv[2] = (char *) s;
  argv[3] = NULL;

  if ((pid = vfork ()) == 0)
    {
      /* ??? It's not clear what's the right path to take (pun intended :-).
	 There won't be an "sh" in any fixed location so we need each user
	 to be able to say where to find "sh".  That suggests using an
	 environment variable, but after a few more such situations we may
	 have too many of them.  */
      char *sh = getenv ("SH_PATH");
      if (sh == NULL)
	sh = "/bin/sh";
      _execve (sh, argv, environ);
      exit (100);
    }
  else if (pid == -1)
    return -1;
  else
    {
      extern int _wait (int *);
      int rc = _wait (&status);
      if (rc == -1)
	return -1;
      status = (status >> 8) & 0xff;
      return status;
    }
}
#endif