1 /* Copyright 2008-2019 Bernhard R. Fischer, Daniel Haslinger.
2  *
3  * This file is part of OnionCat.
4  *
5  * OnionCat is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * OnionCat is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with OnionCat. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*! \file ocatthread.c
19  *  contains thread management functions. Basically these are
20  *  wrapper functions around create_pthread.
21  *
22  *  @author Bernhard R. Fischer <rahra _at_ cypherpunk at>
23  *  \date 2019/09/08
24  */
25 
26 
27 #include "ocat.h"
28 
29 
30 // global thread id var and mutex for thread initializiation
31 static int thread_id_ = 0;
32 static pthread_mutex_t thread_mutex_ = PTHREAD_MUTEX_INITIALIZER;
33 static pthread_cond_t thread_cond_ = PTHREAD_COND_INITIALIZER;
34 static OcatThread_t *octh_ = NULL;
35 
36 
init_ocat_thread_struct(OcatThread_t * th)37 void init_ocat_thread_struct(OcatThread_t *th)
38 {
39    // init ocat thread structure
40    th->handle = pthread_self();
41    pthread_mutex_lock(&thread_mutex_);
42    th->id = thread_id_++;
43    th->next = octh_;
44    octh_ = th;
45    pthread_mutex_unlock(&thread_mutex_);
46    log_debug("_init_ thread %d", th->id);
47 }
48 
49 
init_ocat_thread(const char * name)50 const OcatThread_t *init_ocat_thread(const char *name)
51 {
52    OcatThread_t *th;
53 
54    // get memory for the ocat internal thread structure
55    if (!(th = calloc(1, sizeof(OcatThread_t))))
56    {
57       log_msg(LOG_ERR, "could not get memory for thread struct: \"%s\"", strerror(errno));
58       return NULL;
59    }
60 
61    strlcpy(th->name, name, THREAD_NAME_LEN);
62    init_ocat_thread_struct(th);
63 
64    return th;
65 }
66 
67 
thread_run(void * p)68 void *thread_run(void *p)
69 {
70    OcatThread_t **tl;
71    void *r;
72    sigset_t ss;
73 #ifdef DEBUG
74    int ecnt, icnt;
75    static int exit_cnt_ = 0;
76 #endif
77 
78    // block all signals for the thread
79    sigfillset(&ss);
80    pthread_sigmask(SIG_BLOCK, &ss, NULL);
81 
82    // init internal ocat thread structure
83    init_ocat_thread_struct((OcatThread_t *) p);
84 
85    // call thread entry function
86    log_debug("calling thread entry");
87    r = ((OcatThread_t*)p)->entry(((OcatThread_t*)p)->parm);
88    log_debug("thread function returned");
89 
90    // delete thread struct from list and free memory
91    pthread_mutex_lock(&thread_mutex_);
92    for (tl = &octh_; *tl; tl = &(*tl)->next)
93       if ((*tl)->handle == ((OcatThread_t*)p)->handle)
94          break;
95    //free(p);
96    if ((p = *tl))
97    {
98       *tl = (*tl)->next;
99       free(p);
100    }
101 #ifdef DEBUG
102    ecnt = ++exit_cnt_;
103    icnt = thread_id_;
104 #endif
105    pthread_mutex_unlock(&thread_mutex_);
106 
107    log_debug("_exit_ thread, %d inits, %d exits", icnt, ecnt);
108    return r;
109 }
110 
111 
run_ocat_thread(const char * name,void * (* thfunc)(void *),void * parm)112 int run_ocat_thread(const char *name, void *(*thfunc)(void*), void *parm)
113 {
114    int rc;
115    OcatThread_t *th;
116 
117    // we need a helper structure on startup.
118    // this is because pthread_create pushes only one arg.
119    // the helper struct is freed again from the thread
120    // (within thread_run()).
121    if (!(th = calloc(1, sizeof(OcatThread_t))))
122    {
123       rc = errno;
124       log_msg(LOG_EMERG, "could not create thread %s: \"%s\"", name, strerror(errno));
125       return rc;
126    }
127 
128    strlcpy(th->name, name, THREAD_NAME_LEN);
129    th->entry = thfunc;
130    th->parm = parm;
131 
132    if ((rc = pthread_attr_init(&th->attr)))
133    {
134       log_msg(LOG_ERR, "could not init pthread attr: \"%s\"", strerror(rc));
135       return rc;
136    }
137 
138 #ifdef DEBUG
139    size_t ss;
140    if ((rc - pthread_attr_getstacksize(&th->attr, &ss)))
141       log_debug("could not get thread stack size attr: \"%s\"", strerror(rc));
142    else
143       log_debug("default thread stack size %dk, setting to %dk", ss / 1024, THREAD_STACK_SIZE / 1024);
144 #endif
145 
146    if ((rc - pthread_attr_setstacksize(&th->attr, THREAD_STACK_SIZE)))
147    {
148       log_msg(LOG_EMERG, "could not init thread stack size attr - system may be unstable: \"%s\"", strerror(rc));
149       return rc;
150    }
151 
152    log_debug("starting [%s]", name);
153    if ((rc = pthread_create(&th->handle, &th->attr, thread_run, th)))
154    {
155       log_msg(LOG_EMERG, "could not start thread %s: \"%s\"", name, strerror(rc));
156       free(th);
157    }
158 
159    return rc;
160 }
161 
162 
get_thread(void)163 const OcatThread_t *get_thread(void)
164 {
165    OcatThread_t *th;
166    pthread_t thread = pthread_self();
167 
168    pthread_mutex_lock(&thread_mutex_);
169    for (th = octh_; th; th = th->next)
170       if (th->handle == thread)
171          break;
172    pthread_mutex_unlock(&thread_mutex_);
173 
174    return th;
175 }
176 
177 
set_thread_name(const char * n)178 int set_thread_name(const char *n)
179 {
180    int e = -1;
181    OcatThread_t *th;
182    pthread_t thread = pthread_self();
183 
184    pthread_mutex_lock(&thread_mutex_);
185    for (th = octh_; th; th = th->next)
186       if (th->handle == thread)
187       {
188          strlcpy(th->name, n, THREAD_NAME_LEN);
189          e = 0;
190          break;
191       }
192    pthread_mutex_unlock(&thread_mutex_);
193 
194    return e;
195 }
196 
197 
198 /*! This function waits for a thread identified by name to become ready,
199  * meaning it set its ready flag with set_thread_ready(). If the thread is not
200  * found in the thread list, the function blocks. It wakes up again if any
201  * thread calls set_thread_ready() which reinitiates the reevaluation of the
202  * thread list. A thread my not appear in the thread list because it was not
203  * created, yet.
204  * @param s Pointer to thread name.
205  * @return The function always returns 1.
206  */
wait_thread_by_name_ready(const char * s)207 int wait_thread_by_name_ready(const char *s)
208 {
209    OcatThread_t *th;
210    int e;
211 
212    log_debug("waiting for [%s] to become ready", s);
213    pthread_mutex_lock(&thread_mutex_);
214    for (e = 0; !e; )
215    {
216       // loop over all threads
217       for (th = octh_; th; th = th->next)
218       {
219          // match thread name
220          if (!strcmp(th->name, s))
221          {
222             // check if it is ready
223             while (!th->ready)
224                // and wait if not
225                pthread_cond_wait(&thread_cond_, &thread_mutex_);
226             // set ready flag and break loop
227             e = 1;
228             break;
229          }
230       }
231 
232       // check if ready flag still not set, meaning thread was not found by name
233       if (!e)
234       {
235          pthread_cond_wait(&thread_cond_, &thread_mutex_);
236       }
237    }
238    pthread_mutex_unlock(&thread_mutex_);
239    log_debug("[%s] ready", s);
240 
241    return e;
242 }
243 
244 
set_thread_ready(void)245 int set_thread_ready(void)
246 {
247    int e = -1;
248    OcatThread_t *th;
249    pthread_t thread = pthread_self();
250 
251    log_debug("set_thread_ready()");
252    pthread_mutex_lock(&thread_mutex_);
253    for (th = octh_; th; th = th->next)
254       if (th->handle == thread)
255       {
256          th->ready = 1;
257          pthread_cond_broadcast(&thread_cond_);
258          e = 0;
259          break;
260       }
261    pthread_mutex_unlock(&thread_mutex_);
262 
263    return e;
264 }
265 
266 
print_threads(FILE * f)267 void print_threads(FILE *f)
268 {
269    OcatThread_t *th;
270 
271    pthread_mutex_lock(&thread_mutex_);
272    for (th = octh_; th; th = th->next)
273    {
274       fprintf(f, "[%s] "
275             "handle = 0x%08lx, "
276             "id = %d, "
277             "entry = %p, "
278             "parm = %p, "
279             "detached = %d\n",
280             th->name, (long) th->handle, th->id, th->entry, th->parm, th->detached);
281    }
282    pthread_mutex_unlock(&thread_mutex_);
283 }
284 
285 
join_threads(void)286 int join_threads(void)
287 {
288    OcatThread_t *th, thb;
289    void *ret;
290    int rc;
291 
292    for (;;)
293    {
294       pthread_mutex_lock(&thread_mutex_);
295       for (th = octh_, rc = 0; th && th->detached; th = th->next, rc++);
296       if (!th)
297       {
298          pthread_mutex_unlock(&thread_mutex_);
299          break;
300       }
301       memcpy(&thb, th, sizeof(OcatThread_t));
302       pthread_mutex_unlock(&thread_mutex_);
303 
304       log_debug("joing thread \"%s\" (%d)", thb.name, thb.id);
305       if ((rc = pthread_join(thb.handle, &ret)))
306          log_msg(LOG_ERR, "error joining thread: \"%s\"", strerror(rc));
307       log_debug("thread successful joined and return %p", ret);
308    }
309    log_debug("no more joinable threads available, %d detached", rc);
310    return rc;
311 }
312 
313 
detach_thread(void)314 void detach_thread(void)
315 {
316    OcatThread_t *th;
317    pthread_t thread = pthread_self();
318    int rc = 0;
319 
320    pthread_mutex_lock(&thread_mutex_);
321    for (th = octh_; th; th = th->next)
322       if (th->handle == thread)
323          break;
324    if (th && !(rc = pthread_detach(thread)))
325       th->detached = 1;
326    pthread_mutex_unlock(&thread_mutex_);
327 
328    if (!th)
329       log_msg(LOG_EMERG, "thread tries to detach but is not in list");
330    else if (rc)
331       log_msg(LOG_ERR, "could not detach thread: \"%s\"", strerror(rc));
332    else
333       log_debug("thread detached");
334 }
335 
336 
337 /*! Check for termination request.
338  *  @return 1 if termination requested, otherwise 0.
339  */
term_req(void)340 int term_req(void)
341 {
342    int trq;
343 
344    lock_setup();
345    trq = CNF(term_req);
346    unlock_setup();
347 
348    return trq;
349 }
350 
351 
352 /*! Set termination request. */
set_term_req(void)353 void set_term_req(void)
354 {
355    lock_setup();
356    CNF(term_req) = 1;
357    unlock_setup();
358 }
359 
360