cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

exec_tools.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 Northern.tech AS
3 
4  This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the
8  Free Software Foundation; version 3.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 
19  To the extent this program is licensed as part of the Enterprise
20  versions of CFEngine, the applicable Commercial Open Source License
21  (COSL) may apply to this file if you as a licensee so wish it. See
22  included file COSL.txt.
23 */
24 
25 #include <exec_tools.h>
26 
27 #include <files_names.h>
28 #include <files_interfaces.h>
29 #include <pipes.h>
30 #include <string_lib.h>
31 #include <misc_lib.h>
32 #include <file_lib.h>
33 #include <generic_agent.h> // CloseLog
34 
35 /********************************************************************/
36 
37 bool GetExecOutput(const char *command, char **buffer, size_t *buffer_size, ShellType shell)
38 /* Buffer initially contains whole exec string */
39 {
40  FILE *pp;
41 
42  if (shell == SHELL_TYPE_USE)
43  {
44  pp = cf_popen_sh(command, "rt");
45  }
46  else if (shell == SHELL_TYPE_POWERSHELL)
47  {
48 #ifdef __MINGW32__
49  pp = cf_popen_powershell(command, "rt");
50 #else // !__MINGW32__
51  Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows");
52  return false;
53 #endif // __MINGW32__
54  }
55  else
56  {
57  pp = cf_popen(command, "rt", true);
58  }
59 
60  if (pp == NULL)
61  {
62  Log(LOG_LEVEL_ERR, "Couldn't open pipe to command '%s'. (cf_popen: %s)", command, GetErrorStr());
63  return false;
64  }
65 
66  size_t offset = 0;
67  size_t line_size = CF_EXPANDSIZE;
68  size_t attempted_size = 0;
69  char *line = xcalloc(1, line_size);
70 
71  while (*buffer_size < CF_MAXSIZE)
72  {
73  ssize_t res = CfReadLine(&line, &line_size, pp);
74  if (res == -1)
75  {
76  if (!feof(pp))
77  {
78  Log(LOG_LEVEL_ERR, "Unable to read output of command '%s'. (fread: %s)", command, GetErrorStr());
79  cf_pclose(pp);
80  free(line);
81  return false;
82  }
83  else
84  {
85  break;
86  }
87  }
88 
89  if ((attempted_size = snprintf(*buffer + offset, *buffer_size - offset, "%s\n", line)) >= *buffer_size - offset)
90  {
91  *buffer_size += (attempted_size > CF_EXPANDSIZE ? attempted_size : CF_EXPANDSIZE);
92  *buffer = xrealloc(*buffer, *buffer_size);
93  snprintf(*buffer + offset, *buffer_size - offset, "%s\n", line);
94  }
95 
96  offset += strlen(line) + 1;
97  }
98 
99  if (offset > 0)
100  {
101  if (Chop(*buffer, *buffer_size) == -1)
102  {
103  Log(LOG_LEVEL_ERR, "Chop was called on a string that seemed to have no terminator");
104  }
105  }
106 
107  Log(LOG_LEVEL_DEBUG, "GetExecOutput got '%s'", *buffer);
108 
109  cf_pclose(pp);
110  free(line);
111  return true;
112 }
113 
114 /**********************************************************************/
115 
117 {
118  int fd;
119 
120 #ifdef HAVE_SETSID
121  if (setsid() == (pid_t) -1)
122  {
124  "Failed to become a session leader while daemonising (setsid: %s)",
125  GetErrorStr());
126  }
127 #endif
128 
129  CloseNetwork();
130  CloseLog();
131 
132  fflush(NULL);
133 
134  /* Close descriptors 0,1,2 and reopen them with /dev/null. */
135 
136  fd = open(NULLFILE, O_RDWR, 0);
137  if (fd == -1)
138  {
139  Log(LOG_LEVEL_WARNING, "Could not open '%s', "
140  "stdin/stdout/stderr are still open (open: %s)",
141  NULLFILE, GetErrorStr());
142  }
143  else
144  {
145  if (dup2(fd, STDIN_FILENO) == -1)
146  {
148  "Could not close stdin while daemonising (dup2: %s)",
149  GetErrorStr());
150  }
151 
152  if (dup2(fd, STDOUT_FILENO) == -1)
153  {
155  "Could not close stdout while daemonising (dup2: %s)",
156  GetErrorStr());
157  }
158 
159  if (dup2(fd, STDERR_FILENO) == -1)
160  {
162  "Could not close stderr while daemonising (dup2: %s)",
163  GetErrorStr());
164  }
165 
166  if (fd > STDERR_FILENO)
167  {
168  close(fd);
169  }
170  }
171 
172  if (chdir("/"))
173  {
175  "Failed to chdir into '/' directory while daemonising (chdir: %s)",
176  GetErrorStr());
177  }
178 }
179 
180 /**********************************************************************/
181 
182 /**
183  * Split the command string like "/bin/echo -n Hi!" in two parts -- the
184  * executable ("/bin/echo") and the arguments for the executable ("-n Hi!").
185  *
186  * @param[in] comm the whole command to split
187  * @param[out] exec pointer to **a newly allocated** string with the executable
188  * @param[out] args pointer to **a newly allocated** string with the args
189  *
190  * @note Whitespace between the executable and the arguments is skipped.
191  */
192 void ArgGetExecutableAndArgs(const char *comm, char **exec, char **args)
193 {
194  const char *s = comm;
195  while (*s != '\0')
196  {
197  const char *end = NULL;
198 
199  if (isspace((int)*s)) /* Skip whitespace */
200  {
201  s++;
202  continue;
203  }
204 
205  switch (*s)
206  {
207  case '"': /* Look for matching quote */
208  case '\'':
209  case '`':
210  {
211  char delim = *(s++); /* Skip first delimeter */
212 
213  end = strchr(s, delim);
214  break;
215  }
216  default: /* Look for whitespace */
217  end = strpbrk(s, " \f\n\r\t\v");
218  break;
219  }
220 
221  if (end == NULL) /* Delimeter was not found, remaining string is the executable */
222  {
223  *exec = xstrdup(s);
224  *args = NULL;
225  return;
226  }
227  else
228  {
229  assert(end > s);
230  const size_t length = end - s;
231  *exec = xstrndup(s, length);
232 
233  const char *args_start = end;
234  if (*(args_start + 1) != '\0')
235  {
236  args_start++; /* Skip second delimeter */
237  args_start += strspn(args_start, " \f\n\r\t\v"); /* Skip whitespace */
238  *args = xstrdup(args_start);
239  }
240  else
241  {
242  *args = NULL;
243  }
244  return;
245  }
246  }
247 
248  /* was not able to parse/split the command */
249  *exec = NULL;
250  *args = NULL;
251  return;
252 }
253 
254 #define INITIAL_ARGS 8
255 
256 char **ArgSplitCommand(const char *comm)
257 {
258  const char *s = comm;
259 
260  int argc = 0;
261  int argslen = INITIAL_ARGS;
262  char **args = xmalloc(argslen * sizeof(char *));
263 
264  while (*s != '\0')
265  {
266  const char *end;
267  char *arg;
268 
269  if (isspace((int)*s)) /* Skip whitespace */
270  {
271  s++;
272  continue;
273  }
274 
275  switch (*s)
276  {
277  case '"': /* Look for matching quote */
278  case '\'':
279  case '`':
280  {
281  char delim = *s++; /* Skip first delimeter */
282 
283  end = strchr(s, delim);
284  break;
285  }
286  default: /* Look for whitespace */
287  end = strpbrk(s, " \f\n\r\t\v");
288  break;
289  }
290 
291  if (end == NULL) /* Delimeter was not found, remaining string is the argument */
292  {
293  arg = xstrdup(s);
294  s += strlen(arg);
295  }
296  else
297  {
298  arg = xstrndup(s, end - s);
299  s = end;
300  if ((*s == '"') || (*s == '\'') || (*s == '`')) /* Skip second delimeter */
301  s++;
302  }
303 
304  /* Argument */
305 
306  if (argc == argslen)
307  {
308  argslen *= 2;
309  args = xrealloc(args, argslen * sizeof(char *));
310  }
311 
312  args[argc++] = arg;
313  }
314 
315 /* Trailing NULL */
316 
317  if (argc == argslen)
318  {
319  argslen += 1;
320  args = xrealloc(args, argslen * sizeof(char *));
321  }
322  args[argc++] = NULL;
323 
324  return args;
325 }
326 
327 /**********************************************************************/
328 
329 void ArgFree(char **args)
330 {
331  if (args != NULL)
332  {
333  for (char **arg = args; *arg; ++arg)
334  {
335  free(*arg);
336  }
337  free(args);
338  }
339 }
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
void * xcalloc(size_t nmemb, size_t size)
Definition: alloc-mini.c:51
char * xstrdup(const char *str)
Definition: alloc-mini.c:56
void * xrealloc(void *ptr, size_t size)
Definition: alloc.c:51
char * xstrndup(const char *str, size_t n)
Definition: alloc.c:61
#define NULLFILE
Definition: cf3.defs.h:281
ShellType
Definition: cf3.defs.h:1105
@ SHELL_TYPE_USE
Definition: cf3.defs.h:1107
@ SHELL_TYPE_POWERSHELL
Definition: cf3.defs.h:1108
void free(void *)
#define CF_EXPANDSIZE
Definition: definitions.h:51
#define CF_MAXSIZE
Definition: definitions.h:31
void ArgGetExecutableAndArgs(const char *comm, char **exec, char **args)
Definition: exec_tools.c:192
#define INITIAL_ARGS
Definition: exec_tools.c:254
char ** ArgSplitCommand(const char *comm)
Definition: exec_tools.c:256
void ActAsDaemon()
Definition: exec_tools.c:116
void ArgFree(char **args)
Definition: exec_tools.c:329
bool GetExecOutput(const char *command, char **buffer, size_t *buffer_size, ShellType shell)
Definition: exec_tools.c:37
ssize_t CfReadLine(char **buff, size_t *size, FILE *fp)
Works exactly like posix 'getline', EXCEPT it does not include carriage return at the end.
Definition: file_lib.c:1476
void CloseLog(void)
#define NULL
Definition: getopt1.c:56
const char * GetErrorStr(void)
Definition: logging.c:275
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_WARNING
Definition: logging.h:43
int cf_pclose(FILE *pp)
Definition: pipes_unix.c:812
FILE * cf_popen(const char *command, const char *type, bool capture_stderr)
Definition: pipes_unix.c:332
FILE * cf_popen_sh(const char *command, const char *type)
Definition: pipes_unix.c:560
#define CloseNetwork()
Definition: prototypes3.h:102
int Chop(char *str, size_t max_length)
Remove trailing spaces.
Definition: string_lib.c:1174