Imported Upstream version 0.3.3 - merge fix
[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 template<class T>
54 char** dupSysStringArray(T const& array)
55 {
56   char** new_array;
57   new_array = static_cast<char**>(malloc((array.size() + 1)*sizeof(char*)));
58   if(!new_array)
59     return NULL;
60
61   unsigned int i = 0;
62   for(typename T::const_iterator it = array.begin(); it != array.end(); ++it) {
63     new_array[i] = strdup(it->c_str());
64     if(!new_array) {
65       while(i--)
66         free(new_array[i]);
67       free(new_array);
68       return NULL;
69     }
70     ++i;
71   }
72   new_array[array.size()] = NULL;
73   return new_array;
74 }
75
76 void freeSysStringArray(char** array)
77 {
78   if(!array)
79     return;
80
81   for(int i=0; array[i] ; ++i)
82     free(array[i]);
83
84   free(array);
85 }
86
87 void SysExec::doExec(StringVector args, StringList env)
88 {
89   int pipefd[2];
90   if(pipe(pipefd) == -1) {
91     cLog.msg(Log::PRIO_ERROR) << "executing script '" << script_ << "' pipe() error: " << AnytunErrno(errno);
92     return;
93   }
94
95   pid_ = fork();
96   if(pid_ == -1) {
97     cLog.msg(Log::PRIO_ERROR) << "executing script '" << script_ << "' fork() error: " << AnytunErrno(errno);
98     return;
99   }
100
101   if(pid_) {
102     close(pipefd[1]);
103     pipefd_=pipefd[0];
104     // parent exits here, call waitForScript to cleanup up zombie
105     return;
106   }
107   // child code, exec the script
108   int fd;
109   for (fd=getdtablesize();fd>=0;--fd) // close all file descriptors
110     if(fd != pipefd[1]) close(fd);
111   
112   fd = open("/dev/null",O_RDWR);        // stdin
113   if(fd == -1)
114     cLog.msg(Log::PRIO_WARNING) << "can't open stdin";
115   else {
116     if(dup(fd) == -1)   // stdout
117       cLog.msg(Log::PRIO_WARNING) << "can't open stdout";
118     if(dup(fd) == -1)   // stderr
119       cLog.msg(Log::PRIO_WARNING) << "can't open stderr";
120   }
121   
122   args.insert(args.begin(), script_);
123   char** argv = dupSysStringArray(args);
124   char** evp = dupSysStringArray(env);
125   
126   execve(script_.c_str(), argv, evp);
127       // if execve returns, an error occurred, but logging doesn't work 
128       // because we closed all file descriptors, so just write errno to
129       // pipe and call exit
130   
131   freeSysStringArray(argv);
132   freeSysStringArray(evp);
133
134   int err = errno;
135   int ret = write(pipefd[1], (void*)(&err), sizeof(err));
136   if(ret != sizeof(errno))
137     exit(-2);
138   exit(-1);
139 }
140
141 int SysExec::waitForScript()
142 {
143   int status = 0;
144   waitpid(pid_, &status, 0);
145
146   fd_set rfds;
147   FD_ZERO(&rfds);
148   FD_SET(pipefd_, &rfds);
149   struct timeval tv = { 0 , 0 };
150   if(select(pipefd_+1, &rfds, NULL, NULL, &tv) == 1) {
151     int err = 0;
152     if(read(pipefd_, (void*)(&err), sizeof(err)) >= static_cast<int>(sizeof(err))) {
153       cLog.msg(Log::PRIO_ERROR) << "script '" << script_ << "' exec() error: " << AnytunErrno(err);
154       close(pipefd_);
155       return_code_ = -1;
156       return return_code_;
157     }
158   }
159
160   close(pipefd_);
161   closed_ = true;
162   return_code_ = status;
163
164   return return_code_;
165 }
166
167 void SysExec::waitAndDestroy(SysExec*& s)
168 {
169   if(!s)
170     return;
171
172   s->waitForScript();
173   if(WIFEXITED(s->return_code_))
174     cLog.msg(Log::PRIO_NOTICE) << "script '" << s->script_ << "' returned " << WEXITSTATUS(s->return_code_);  
175   else if(WIFSIGNALED(s->return_code_))
176     cLog.msg(Log::PRIO_NOTICE) << "script '" << s->script_ << "' terminated after signal " << WTERMSIG(s->return_code_);
177   else if(WIFSTOPPED(s->return_code_))
178     cLog.msg(Log::PRIO_NOTICE) << "script '" << s->script_ << "' stopped after signal " << WSTOPSIG(s->return_code_);
179   else if(WIFCONTINUED(s->return_code_))
180     cLog.msg(Log::PRIO_NOTICE) << "script '" << s->script_ << "' continued after SIGCONT";
181
182   delete(s);
183   s = NULL;
184 }
185
186 #endif // ANYTUN_sysexec_hpp_INCLUDED