1 /*
2 
3                           Firewall Builder
4 
5                  Copyright (C) 2000 NetCitadel, LLC
6 
7   Author:  Vadim Kurland     vadim@fwbuilder.org
8            Vadim Zaliva      lord@crocodile.org
9 
10   $Id$
11 
12 
13   This program is free software which we release under the GNU General Public
14   License. You may redistribute and/or modify this program under the terms
15   of that license as published by the Free Software Foundation; either
16   version 2 of the License, or (at your option) any later version.
17 
18   This program is distributed in the hope that it will be useful,
19   but WITHOUT ANY WARRANTY; without even the implied warranty of
20   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21   GNU General Public License for more details.
22 
23   To get a copy of the GNU General Public License, write to the Free Software
24   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 
26 */
27 
28 #include "config.h"
29 #include "fwbuilder/libfwbuilder-config.h"
30 
31 
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <vector>
35 #include <iostream>
36 #include <pthread.h>
37 
38 // #include <unistd.h>
39 #include <sys/types.h>
40 #include <errno.h>
41 #include <signal.h>
42 
43 #include "fwbuilder/BackgroundOp.h"
44 
45 using namespace std;
46 using namespace libfwbuilder;
47 
48 
BackgroundOp()49 BackgroundOp::BackgroundOp():running(false),connected(true)
50 {
51     error        = NULL;
52     stop_program = new SyncFlag(false);
53     iamdead      = new SyncFlag(false);
54     pthread_attr_init(&tattr);
55 }
56 
57 /*
58  * we destroy logger in the background thread because run_impl may use
59  * logger and we should be able to destroy object of this class even
60  * while background thread is working.
61  *
62  * iamdead should not be deleted either because it is used as a flag
63  * so that background thread would know if this object has been deleted
64  * while run_impl was running
65  */
~BackgroundOp()66 BackgroundOp::~BackgroundOp()
67 {
68     stop_program->lock();
69     stop_program->modify(true);
70     stop_program->unlock();
71 
72     iamdead->lock();
73     iamdead->modify(true);
74     iamdead->unlock();
75 
76     pthread_attr_destroy(&tattr);
77 }
78 
79 /* replaced by a macro
80 void BackgroundOp::check_stop()
81 {
82     stop_program->lock();
83     if ( stop_program->peek() )
84     {
85         stop_program->unlock();
86         pthread_exit(NULL);
87     }
88     stop_program->unlock();
89 }
90 */
91 
isRunning()92 bool BackgroundOp::isRunning()     { return running;  }
setRunning()93 void BackgroundOp::setRunning()    { running=true;    }
clearRunning()94 void BackgroundOp::clearRunning()  { running=false;   }
95 
isConnected()96 bool BackgroundOp::isConnected()   { return connected; }
disconnect()97 void BackgroundOp::disconnect()    { connected=false;  }
98 
99 
100 
start_operation()101 Logger* BackgroundOp::start_operation() throw(FWException)
102 {
103     /*
104      * Suppose calling program wants to stop background operation. It
105      * calls stop_oprtation and some times immediately destroys
106      * BackgroundOp object. If background op. was stuck in a system
107      * call when this happened, it will check stop_program flag only
108      * after it returns from the system call. Since BackgroundOp
109      * object has been destroyed by then, the program crashes. To
110      * avoid this, we create stop_program flag here so that even
111      * BackgroundOp object is destoryed, this flag is still available
112      * and can be properly checked.
113      */
114     stop_program->lock();
115     stop_program->modify(false);
116     stop_program->unlock();
117 
118     running = true;
119 
120     Logger *logger  = new QueueLogger();
121 
122     void **void_pair = new void*[4];
123     void_pair[0] = this;
124     void_pair[1] = logger;
125     void_pair[2] = iamdead;
126     void_pair[3] = stop_program;
127 
128     pthread_t tid;
129     pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
130     int err = pthread_create(&tid, &tattr, background_thread, void_pair);
131     switch (err)
132     {
133     case EAGAIN:
134         throw FWException("Not enough system resources to create new thread");
135     case EINVAL:
136         throw FWException("The value specified by attr is invalid.");
137     }
138     return logger;
139 }
140 
stop_operation()141 void BackgroundOp::stop_operation()
142 {
143     error = new FWException("Interrupted by user");
144     stop_program->lock();
145     stop_program->modify(true);
146     stop_program->unlock();
147 }
148 
149 namespace libfwbuilder
150 {
background_thread(void * args)151 void *background_thread(void *args)
152 {
153     void **void_pair=(void**)args;
154 
155     BackgroundOp *bop          = (BackgroundOp*)void_pair[0];
156     Logger       *logger       = (Logger *)     void_pair[1];
157     SyncFlag     *isdead       = (SyncFlag*)    void_pair[2];
158     SyncFlag     *stop_program = (SyncFlag*)    void_pair[3];
159 
160     try
161     {
162 	bop->run_impl(logger,stop_program);
163     } catch (FWException &ex)
164     {
165         isdead->lock();
166         if (isdead->peek())
167         {
168             isdead->unlock();
169             delete logger;
170             delete isdead;
171             delete void_pair;
172             return(NULL);
173         }
174         *logger << "Exception: " << ex.toString().c_str() << '\n';
175         bop->error=new FWException(ex);
176         isdead->unlock();
177     }
178 
179     *logger << "Background process has finished\n";
180 
181     isdead->lock();
182     if (isdead->peek())
183     {
184         isdead->unlock();
185         delete logger;
186         delete isdead;
187         delete void_pair;
188         return(NULL);
189     }
190 
191 /* operation completed - clear "running" flag */
192     bop->clearRunning();
193 
194     isdead->unlock();
195 
196 /* wait till the other thread reads all the lines from logger. If
197  * widget that was reading lines from this logger has been destroyed,
198  * it should have called BackhroundOp::disconnect to release BackgroundOp
199  * object
200  */
201 
202     while (true) {
203 
204         isdead->lock();
205         if (isdead->peek() ||  !bop->isConnected())
206         {
207             isdead->unlock();
208             break;
209         }
210         isdead->unlock();
211 
212         libfwbuilder::cxx_sleep(1);
213     }
214 
215     delete logger;
216     delete void_pair;
217 
218     return(NULL);
219 }
220 
221 }
222 
223 
224 
225 
226 
227