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)  

pipes.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 <pipes.h>
26 #include <rlist.h>
27 #include <buffer.h>
28 #include <signals.h>
29 #include <string_lib.h>
30 
31 bool PipeTypeIsOk(const char *type)
32 {
33  if (type[0] != 'r' && type[0] != 'w')
34  {
35  return false;
36  }
37  else if (type[1] != 't' && type[1] != '+')
38  {
39  if (type[1] == '\0')
40  {
41  return true;
42  }
43  else
44  {
45  return false;
46  }
47  }
48  else if (type[2] == '\0' || type[2] == 't')
49  {
50  return true;
51  }
52  else
53  {
54  return false;
55  }
56 }
57 
58 /*******************************************************************/
59 /* Pipe read/write interface, originally in package modules */
60 /*******************************************************************/
61 
62 Rlist *PipeReadData(const IOData *io, int pipe_timeout_secs, int pipe_termination_check_secs)
63 {
64  char buff[CF_BUFSIZE] = {0};
65 
66  Buffer *data = BufferNew();
67  if (!data)
68  {
70  "Unable to allocate buffer for handling pipe responses.");
71  return NULL;
72  }
73 
74  int timeout_seconds_left = pipe_timeout_secs;
75 
76  while (!IsPendingTermination() && timeout_seconds_left > 0)
77  {
78  int fd = PipeIsReadWriteReady(io, pipe_termination_check_secs);
79 
80  if (fd < 0)
81  {
83  "Error reading data from application pipe %d", fd);
84  break;
85  }
86  else if (fd == io->read_fd)
87  {
88  ssize_t res = read(fd, buff, sizeof(buff) - 1);
89  if (res == -1)
90  {
91  if (errno == EINTR)
92  {
93  continue;
94  }
95  else
96  {
98  "Unable to read output from application pipe: %s",
99  GetErrorStr());
100  BufferDestroy(data);
101  return NULL;
102  }
103  }
104  else if (res == 0) /* reached EOF */
105  {
106  break;
107  }
108  Log(LOG_LEVEL_DEBUG, "Data read from application pipe: %zd [%s]",
109  res, buff);
110 
111  BufferAppendString(data, buff);
112  memset(buff, 0, sizeof(buff));
113  }
114  else if (fd == 0) /* timeout */
115  {
116  timeout_seconds_left -= pipe_termination_check_secs;
117  continue;
118  }
119  }
120 
121  char *read_string = BufferClose(data);
122 
123 #ifdef __MINGW32__
124  bool detect_crlf = true;
125 #else
126  bool detect_crlf = false;
127 #endif
128 
129  Rlist *response_lines = RlistFromStringSplitLines(read_string, detect_crlf);
130  free(read_string);
131 
132  return response_lines;
133 }
134 
135 int PipeWrite(IOData *io, const char *data)
136 {
137  /* If there is nothing to write close writing end of pipe. */
138  if (data == NULL || strlen(data) == 0)
139  {
140  if (io->write_fd >= 0)
141  {
143  io->write_fd = -1;
144  }
145  return 0;
146  }
147 
148  ssize_t wrt = write(io->write_fd, data, strlen(data));
149 
150  /* Make sure to close write_fd after sending all data. */
151  if (io->write_fd >= 0)
152  {
154  io->write_fd = -1;
155  }
156  return wrt;
157 }
158 
159 int PipeWriteData(const char *base_cmd, const char *args, const char *data)
160 {
161  assert(base_cmd);
162  assert(args);
163 
164  char *command = StringFormat("%s %s", base_cmd, args);
165  IOData io = cf_popen_full_duplex(command, false, true);
166  free(command);
167 
168  if (io.write_fd == -1 || io.read_fd == -1)
169  {
170  Log(LOG_LEVEL_VERBOSE, "Error occurred while opening pipes for "
171  "communication with application '%s'.", base_cmd);
172  return -1;
173  }
174 
175  Log(LOG_LEVEL_DEBUG, "Opened fds %d and %d for command '%s'.",
176  io.read_fd, io.write_fd, args);
177 
178  int res = 0;
179  if (PipeWrite(&io, data) != strlen(data))
180  {
182  "Was not able to send whole data to application '%s'.",
183  base_cmd);
184  res = -1;
185  }
186 
187  /* If script returns non 0 status */
188  int close = cf_pclose_full_duplex(&io);
189  if (close != EXIT_SUCCESS)
190  {
192  "Application '%s' returned with non zero return code: %d",
193  base_cmd, close);
194  res = -1;
195  }
196  return res;
197 }
198 
199 /* In some cases the response is expected to be not filled out. Some requests
200  will have response filled only in case of errors. */
201 int PipeReadWriteData(const char *base_cmd, const char *args, const char *request,
202  Rlist **response, int pipe_timeout_secs, int pipe_termination_check_secs)
203 {
204  assert(base_cmd);
205  assert(args);
206 
207  char *command = StringFormat("%s %s", base_cmd, args);
208  IOData io = cf_popen_full_duplex(command, false, true);
209 
210  if (io.write_fd == -1 || io.read_fd == -1)
211  {
212  Log(LOG_LEVEL_INFO, "Some error occurred while communicating with %s", command);
213  free(command);
214  return -1;
215  }
216 
217  Log(LOG_LEVEL_DEBUG, "Opened fds %d and %d for command '%s'.",
218  io.read_fd, io.write_fd, command);
219 
220  if (PipeWrite(&io, request) != strlen(request))
221  {
222  Log(LOG_LEVEL_VERBOSE, "Couldn't send whole data to application '%s'.",
223  base_cmd);
224  free(command);
225  return -1;
226  }
227 
228  /* We can have some error message here. */
229  Rlist *res = PipeReadData(&io, pipe_timeout_secs, pipe_termination_check_secs);
230 
231  /* If script returns non 0 status */
232  int close = cf_pclose_full_duplex(&io);
233  if (close != EXIT_SUCCESS)
234  {
236  "Command '%s' returned with non zero return code: %d",
237  command, close);
238  free(command);
239  RlistDestroy(res);
240  return -1;
241  }
242 
243  free(command);
244  *response = res;
245  return 0;
246 }
void BufferDestroy(Buffer *buffer)
Destroys a buffer and frees the memory associated with it.
Definition: buffer.c:72
Buffer * BufferNew(void)
Buffer initialization routine.
Definition: buffer.c:48
char * BufferClose(Buffer *buffer)
Destroys a buffer structure returning the its contents.
Definition: buffer.c:81
void BufferAppendString(Buffer *buffer, const char *str)
Definition: buffer.c:246
void free(void *)
#define CF_BUFSIZE
Definition: definitions.h:50
int errno
#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_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
int PipeWrite(IOData *io, const char *data)
Definition: pipes.c:135
int PipeWriteData(const char *base_cmd, const char *args, const char *data)
Definition: pipes.c:159
Rlist * PipeReadData(const IOData *io, int pipe_timeout_secs, int pipe_termination_check_secs)
Definition: pipes.c:62
int PipeReadWriteData(const char *base_cmd, const char *args, const char *request, Rlist **response, int pipe_timeout_secs, int pipe_termination_check_secs)
Definition: pipes.c:201
bool PipeTypeIsOk(const char *type)
Definition: pipes.c:31
int cf_pclose_full_duplex_side(int fd)
Definition: pipes_unix.c:852
IOData cf_popen_full_duplex(const char *command, bool capture_stderr, bool require_full_path)
Definition: pipes_unix.c:242
int PipeIsReadWriteReady(const IOData *io, int timeout_sec)
Definition: pipes_unix.c:995
int cf_pclose_full_duplex(IOData *data)
Definition: pipes_unix.c:880
Rlist * RlistFromStringSplitLines(const char *string, bool detect_crlf)
Definition: rlist.c:1105
void RlistDestroy(Rlist *rl)
Definition: rlist.c:501
bool IsPendingTermination(void)
Definition: signals.c:35
char * StringFormat(const char *fmt,...)
Format string like sprintf and return formatted string allocated on heap as a return value.
Definition: string_lib.c:51
Definition: buffer.h:50
Definition: pipes.h:31
int read_fd
Definition: pipes.h:33
int write_fd
Definition: pipes.h:32
Definition: rlist.h:35