Merge commit 'upstream/0.3.4'
[anytun.git] / src / posix / sysExec.hpp
1 /*
2  *  anytun
3  *
4  *  The secure anycast tunneling protocol (satp) defines a protocol used
5  *  for communication between any combination of unicast and anycast
6  *  tunnel endpoints.  It has less protocol overhead than IPSec in Tunnel
7  *  mode and allows tunneling of every ETHER TYPE protocol (e.g.
8  *  ethernet, ip, arp ...). satp directly includes cryptography and
9  *  message authentication based on the methodes used by SRTP.  It is
10  *  intended to deliver a generic, scaleable and secure solution for
11  *  tunneling and relaying of packets of any protocol.
12  *
13  *
14  *  Copyright (C) 2007-2009 Othmar Gsenger, Erwin Nindl,
15  *                          Christian Pointner <satp@wirdorange.org>
16  *
17  *  This file is part of Anytun.
18  *
19  *  Anytun is free software: you can redistribute it and/or modify
20  *  it under the terms of the GNU General Public License as published by
21  *  the Free Software Foundation, either version 3 of the License, or
22  *  any later version.
23  *
24  *  Anytun is distributed in the hope that it will be useful,
25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  *  GNU General Public License for more details.
28  *
29  *  You should have received a copy of the GNU General Public License
30  *  along with anytun.  If not, see <http://www.gnu.org/licenses/>.
31  */
32 #pragma once
33 #ifndef ANYTUN_sysexec_hpp_INCLUDED
34 #define ANYTUN_sysexec_hpp_INCLUDED
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <sys/wait.h>
41 #include <sys/select.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <cstring>
45
46 SysExec::~SysExec()
47 {
48   if(!closed_) {
49     close(pipefd_);
50   }
51 }
52
53
54 template<class T>
55 char** dupSysStringArray(T const& array)
56 {
57   char** new_array;
58   new_array = static_cast<char**>(malloc((array.size() + 1)*sizeof(char*)));
59   if(!new_array) {
60     return NULL;
61   }
62
63   unsigned int i = 0;
64   for(typename T::const_iterator it = array.begin(); it != array.end(); ++it) {
65     new_array[i] = strdup(it->c_str());
66     if(!new_array) {
67       while(i--) {
68         free(new_array[i]);
69       }
70       free(new_array);
71       return NULL;
72     }
73     ++i;
74   }
75   new_array[array.size()] = NULL;
76   return new_array;
77 }
78
79 void freeSysStringArray(char** array)
80 {
81   if(!array) {
82     return;
83   }
84
85   for(int i=0; array[i] ; ++i) {
86     free(array[i]);
87   }
88
89   free(array);
90 }
91
92 void SysExec::doExec(StringVector args, StringList env)
93 {
94   int pipefd[2];
95   if(pipe(pipefd) == -1) {
96     cLog.msg(Log::PRIO_ERROR) << "executing script '" << script_ << "' pipe() error: " << AnytunErrno(errno);
97     return;
98   }
99
100   pid_ = fork();
101   if(pid_ == -1) {
102     cLog.msg(Log::PRIO_ERROR) << "executing script '" << script_ << "' fork() error: " << AnytunErrno(errno);
103     return;
104   }
105
106   if(pid_) {
107     close(pipefd[1]);
108     pipefd_=pipefd[0];
109     // parent exits here, call waitForScript to cleanup up zombie
110     return;
111   }
112   // child code, exec the script
113   int fd;
114   for(fd=getdtablesize(); fd>=0; --fd) // close all file descriptors
115     if(fd != pipefd[1]) { close(fd); }
116
117   fd = open("/dev/null",O_RDWR);        // stdin
118   if(fd == -1) {
119     cLog.msg(Log::PRIO_WARNING) << "can't open stdin";
120   } else {
121     if(dup(fd) == -1) { // stdout
122       cLog.msg(Log::PRIO_WARNING) << "can't open stdout";
123     }
124     if(dup(fd) == -1) { // stderr
125       cLog.msg(Log::PRIO_WARNING) << "can't open stderr";
126     }
127   }
128
129   args.insert(args.begin(), script_);
130   char** argv = dupSysStringArray(args);
131   char** evp = dupSysStringArray(env);
132
133   execve(script_.c_str(), argv, evp);
134   // if execve returns, an error occurred, but logging doesn't work
135   // because we closed all file descriptors, so just write errno to
136   // pipe and call exit
137
138   freeSysStringArray(argv);
139   freeSysStringArray(evp);
140
141   int err = errno;
142   int ret = write(pipefd[1], (void*)(&err), sizeof(err));
143   if(ret != sizeof(errno)) {
144     exit(-2);
145   }
146   exit(-1);
147 }
148
149 int SysExec::waitForScript()
150 {
151   int status = 0;
152   waitpid(pid_, &status, 0);
153
154   fd_set rfds;
155   FD_ZERO(&rfds);
156   FD_SET(pipefd_, &rfds);
157   struct timeval tv = { 0 , 0 };
158   if(select(pipefd_+1, &rfds, NULL, NULL, &tv) == 1) {
159     int err = 0;
160     if(read(pipefd_, (void*)(&err), sizeof(err)) >= static_cast<int>(sizeof(err))) {
161       cLog.msg(Log::PRIO_ERROR) << "script '" << script_ << "' exec() error: " << AnytunErrno(err);
162       close(pipefd_);
163       return_code_ = -1;
164       return return_code_;
165     }
166   }
167
168   close(pipefd_);
169   closed_ = true;
170   return_code_ = status;
171
172   return return_code_;
173 }
174
175 void SysExec::waitAndDestroy(SysExec*& s)
176 {
177   if(!s) {
178     return;
179   }
180
181   s->waitForScript();
182   if(WIFEXITED(s->return_code_)) {
183     cLog.msg(Log::PRIO_NOTICE) << "script '" << s->script_ << "' returned " << WEXITSTATUS(s->return_code_);
184   } else if(WIFSIGNALED(s->return_code_)) {
185     cLog.msg(Log::PRIO_NOTICE) << "script '" << s->script_ << "' terminated after signal " << WTERMSIG(s->return_code_);
186   } else if(WIFSTOPPED(s->return_code_)) {
187     cLog.msg(Log::PRIO_NOTICE) << "script '" << s->script_ << "' stopped after signal " << WSTOPSIG(s->return_code_);
188   } else if(WIFCONTINUED(s->return_code_)) {
189     cLog.msg(Log::PRIO_NOTICE) << "script '" << s->script_ << "' continued after SIGCONT";
190   }
191
192   delete(s);
193   s = NULL;
194 }
195
196 #endif // ANYTUN_sysexec_hpp_INCLUDED