Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/util/sys_popen.c
Line
Count
Source
1
/*
2
 * Unix SMB/CIFS implementation.
3
 *  Samba system utilities
4
 * Copyright (C) Jeremy Allison  2000
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
#include "replace.h"
21
#include "system/wait.h"
22
#include "system/filesys.h"
23
#include <talloc.h>
24
#include "lib/util/sys_popen.h"
25
#include "lib/util/debug.h"
26
27
/**************************************************************************
28
 Wrapper for popen. Safer as it doesn't search a path.
29
 Modified from the glibc sources.
30
 modified by tridge to return a file descriptor. We must kick our FILE* habit
31
****************************************************************************/
32
33
typedef struct _popen_list
34
{
35
  int fd;
36
  pid_t child_pid;
37
  struct _popen_list *next;
38
} popen_list;
39
40
static popen_list *popen_chain;
41
42
int sys_popenv(char * const argl[])
43
0
{
44
0
  int parent_end, child_end;
45
0
  int pipe_fds[2];
46
0
  popen_list *entry = NULL;
47
0
  const char *command = NULL;
48
0
  int ret;
49
50
0
  if (argl == NULL) {
51
0
    errno = EINVAL;
52
0
    return -1;
53
0
  }
54
0
  command = argl[0];
55
56
0
  if (!*command) {
57
0
    errno = EINVAL;
58
0
    return -1;
59
0
  }
60
61
0
  ret = pipe(pipe_fds);
62
0
  if (ret < 0) {
63
0
    DBG_ERR("error opening pipe: %s\n",
64
0
        strerror(errno));
65
0
    return -1;
66
0
  }
67
68
0
  parent_end = pipe_fds[0];
69
0
  child_end = pipe_fds[1];
70
71
0
  entry = talloc_zero(NULL, popen_list);
72
0
  if (entry == NULL) {
73
0
    DBG_ERR("talloc failed\n");
74
0
    goto err_exit;
75
0
  }
76
77
0
  entry->child_pid = fork();
78
79
0
  if (entry->child_pid == -1) {
80
0
    DBG_ERR("fork failed: %s\n", strerror(errno));
81
0
    goto err_exit;
82
0
  }
83
84
0
  if (entry->child_pid == 0) {
85
86
    /*
87
     * Child !
88
     */
89
90
0
    int child_std_end = STDOUT_FILENO;
91
0
    popen_list *p;
92
93
0
    close(parent_end);
94
0
    if (child_end != child_std_end) {
95
0
      dup2 (child_end, child_std_end);
96
0
      close (child_end);
97
0
    }
98
99
    /*
100
     * POSIX.2:  "popen() shall ensure that any streams from previous
101
     * popen() calls that remain open in the parent process are closed
102
     * in the new child process."
103
     */
104
105
0
    for (p = popen_chain; p; p = p->next)
106
0
      close(p->fd);
107
108
0
    ret = execv(argl[0], argl);
109
0
    if (ret == -1) {
110
0
      DBG_ERR("ERROR executing command "
111
0
        "'%s': %s\n", command, strerror(errno));
112
0
    }
113
0
    _exit (127);
114
0
  }
115
116
  /*
117
   * Parent.
118
   */
119
120
0
  close (child_end);
121
122
  /* Link into popen_chain. */
123
0
  entry->next = popen_chain;
124
0
  popen_chain = entry;
125
0
  entry->fd = parent_end;
126
127
0
  return entry->fd;
128
129
0
err_exit:
130
131
0
  TALLOC_FREE(entry);
132
0
  close(pipe_fds[0]);
133
0
  close(pipe_fds[1]);
134
0
  return -1;
135
0
}
136
137
/**************************************************************************
138
 Wrapper for pclose. Modified from the glibc sources.
139
****************************************************************************/
140
141
int sys_pclose(int fd)
142
0
{
143
0
  int wstatus;
144
0
  popen_list **ptr = &popen_chain;
145
0
  popen_list *entry = NULL;
146
0
  pid_t wait_pid;
147
0
  int status = -1;
148
149
  /* Unlink from popen_chain. */
150
0
  for ( ; *ptr != NULL; ptr = &(*ptr)->next) {
151
0
    if ((*ptr)->fd == fd) {
152
0
      entry = *ptr;
153
0
      *ptr = (*ptr)->next;
154
0
      status = 0;
155
0
      break;
156
0
    }
157
0
  }
158
159
0
  if (status < 0 || close(entry->fd) < 0)
160
0
    return -1;
161
162
  /*
163
   * As Samba is catching and eating child process
164
   * exits we don't really care about the child exit
165
   * code, a -1 with errno = ECHILD will do fine for us.
166
   */
167
168
0
  do {
169
0
    wait_pid = waitpid (entry->child_pid, &wstatus, 0);
170
0
  } while (wait_pid == -1 && errno == EINTR);
171
172
0
  TALLOC_FREE(entry);
173
174
0
  if (wait_pid == -1)
175
0
    return -1;
176
0
  return wstatus;
177
0
}