]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
Fix of case from Fileset to FileSet for compatibility with mysql.
[bacula/bacula] / bacula / src / lib / jcr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Manipulation routines for Job Control Records and
30  *  handling of last_jobs_list.
31  *
32  *  Kern E. Sibbald, December 2000
33  *
34  *  Version $Id$
35  *
36  *  These routines are thread safe.
37  *
38  *  The job list routines were re-written in May 2005 to
39  *  eliminate the global lock while traversing the list, and
40  *  to use the dlist subroutines.  The locking is now done
41  *  on the list each time the list is modified or traversed.
42  *  That is it is "micro-locked" rather than globally locked.
43  *  The result is that there is one lock/unlock for each entry
44  *  in the list while traversing it rather than a single lock
45  *  at the beginning of a traversal and one at the end.  This
46  *  incurs slightly more overhead, but effectively eliminates 
47  *  the possibilty of race conditions.  In addition, with the
48  *  exception of the global locking of the list during the
49  *  re-reading of the config file, no recursion is needed.
50  *
51  */
52
53 #include "bacula.h"
54 #include "jcr.h"
55
56 /* External variables we reference */
57 extern time_t watchdog_time;
58
59 /* External referenced functions */
60 void free_bregexps(alist *bregexps);
61
62 /* Forward referenced functions */
63 extern "C" void timeout_handler(int sig);
64 static void jcr_timeout_check(watchdog_t *self);
65 #ifdef TRACE_JCR_CHAIN
66 static void b_lock_jcr_chain(const char *filen, int line);
67 static void b_unlock_jcr_chain(const char *filen, int line);
68 #define lock_jcr_chain() b_lock_jcr_chain(__FILE__, __LINE__);
69 #define unlock_jcr_chain() b_unlock_jcr_chain(__FILE__, __LINE__);
70 #else
71 static void lock_jcr_chain();
72 static void unlock_jcr_chain();
73 #endif
74
75
76 int num_jobs_run;
77 dlist *last_jobs = NULL;
78 const int max_last_jobs = 10;
79  
80 static dlist *jcrs = NULL;            /* JCR chain */
81 static pthread_mutex_t jcr_lock = PTHREAD_MUTEX_INITIALIZER;
82
83 static pthread_mutex_t job_start_mutex = PTHREAD_MUTEX_INITIALIZER;
84
85 static pthread_mutex_t last_jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
86
87
88 void lock_jobs()
89 {
90    P(job_start_mutex);
91 }
92
93 void unlock_jobs()
94 {
95    V(job_start_mutex);
96 }
97
98 void init_last_jobs_list()
99 {
100    JCR *jcr = NULL;
101    struct s_last_job *job_entry = NULL;
102    if (!last_jobs) {
103       last_jobs = New(dlist(job_entry, &job_entry->link));
104    }
105    if (!jcrs) {
106       jcrs = New(dlist(jcr, &jcr->link));
107    }
108 }
109
110 void term_last_jobs_list()
111 {
112    if (last_jobs) {
113       while (!last_jobs->empty()) {
114          void *je = last_jobs->first();
115          last_jobs->remove(je);
116          free(je);
117       }
118       delete last_jobs;
119       last_jobs = NULL;
120    }
121    if (jcrs) {
122       delete jcrs;
123       jcrs = NULL;
124    }
125 }
126
127 bool read_last_jobs_list(int fd, uint64_t addr)
128 {
129    struct s_last_job *je, job;
130    uint32_t num;
131
132    Dmsg1(100, "read_last_jobs seek to %d\n", (int)addr);
133    if (addr == 0 || lseek(fd, (off_t)addr, SEEK_SET) < 0) {
134       return false;
135    }
136    if (read(fd, &num, sizeof(num)) != sizeof(num)) {
137       return false;
138    }
139    Dmsg1(100, "Read num_items=%d\n", num);
140    if (num > 4 * max_last_jobs) {  /* sanity check */
141       return false;
142    }
143    for ( ; num; num--) {
144       if (read(fd, &job, sizeof(job)) != sizeof(job)) {
145          Dmsg1(000, "Read job entry. ERR=%s\n", strerror(errno));
146          return false;
147       }
148       if (job.JobId > 0) {
149          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
150          memcpy((char *)je, (char *)&job, sizeof(job));
151          if (!last_jobs) {
152             init_last_jobs_list();
153          }
154          last_jobs->append(je);
155          if (last_jobs->size() > max_last_jobs) {
156             je = (struct s_last_job *)last_jobs->first();
157             last_jobs->remove(je);
158             free(je);
159          }
160       }
161    }
162    return true;
163 }
164
165 uint64_t write_last_jobs_list(int fd, uint64_t addr)
166 {
167    struct s_last_job *je;
168    uint32_t num;
169
170    Dmsg1(100, "write_last_jobs seek to %d\n", (int)addr);
171    if (lseek(fd, (off_t)addr, SEEK_SET) < 0) {
172       return 0;
173    }
174    if (last_jobs) {
175       /* First record is number of entires */
176       num = last_jobs->size();
177       if (write(fd, &num, sizeof(num)) != sizeof(num)) {
178          Dmsg1(000, "Error writing num_items: ERR=%s\n", strerror(errno));
179          return 0;
180       }
181       foreach_dlist(je, last_jobs) {
182          if (write(fd, je, sizeof(struct s_last_job)) != sizeof(struct s_last_job)) {
183             Dmsg1(000, "Error writing job: ERR=%s\n", strerror(errno));
184             return 0;
185          }
186       }
187    }
188    /* Return current address */
189    ssize_t stat = lseek(fd, 0, SEEK_CUR);
190    if (stat < 0) {
191       stat = 0;
192    }
193    return stat;
194
195 }
196
197 void lock_last_jobs_list()
198 {
199    P(last_jobs_mutex);
200 }
201
202 void unlock_last_jobs_list()
203 {
204    V(last_jobs_mutex);
205 }
206
207 /*
208  * Push a subroutine address into the job end callback stack
209  */
210 void job_end_push(JCR *jcr, void job_end_cb(JCR *jcr,void *), void *ctx)
211 {
212    jcr->job_end_push.append((void *)job_end_cb);
213    jcr->job_end_push.append(ctx);
214 }
215
216 /* Pop each job_end subroutine and call it */
217 static void job_end_pop(JCR *jcr)
218 {
219    void (*job_end_cb)(JCR *jcr, void *ctx);
220    void *ctx;
221    for (int i=jcr->job_end_push.size()-1; i > 0; ) {
222       ctx = jcr->job_end_push.get(i--);
223       job_end_cb = (void (*)(JCR *,void *))jcr->job_end_push.get(i--);
224       job_end_cb(jcr, ctx);
225    }
226 }
227
228 /*
229  * Create a Job Control Record and link it into JCR chain
230  * Returns newly allocated JCR
231  * Note, since each daemon has a different JCR, he passes
232  *  us the size.
233  */
234 JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
235 {
236    JCR *jcr;
237    MQUEUE_ITEM *item = NULL;
238    struct sigaction sigtimer;
239
240    Dmsg0(3400, "Enter new_jcr\n");
241    jcr = (JCR *)malloc(size);
242    memset(jcr, 0, size);
243    jcr->my_thread_id = pthread_self();
244    jcr->msg_queue = New(dlist(item, &item->link));
245    jcr->job_end_push.init(1, false);
246    jcr->sched_time = time(NULL);
247    jcr->daemon_free_jcr = daemon_free_jcr;    /* plug daemon free routine */
248    jcr->init_mutex();
249    jcr->inc_use_count();   
250    jcr->VolumeName = get_pool_memory(PM_FNAME);
251    jcr->VolumeName[0] = 0;
252    jcr->errmsg = get_pool_memory(PM_MESSAGE);
253    jcr->errmsg[0] = 0;
254    /* Setup some dummy values */
255    bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job));
256    jcr->JobId = 0;
257    jcr->JobType = JT_SYSTEM;          /* internal job until defined */
258    jcr->JobLevel = L_NONE;
259    set_jcr_job_status(jcr, JS_Created);       /* ready to run */
260
261    sigtimer.sa_flags = 0;
262    sigtimer.sa_handler = timeout_handler;
263    sigfillset(&sigtimer.sa_mask);
264    sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
265
266    /*
267     * Locking jobs is a global lock that is needed
268     * so that the Director can stop new jobs from being
269     * added to the jcr chain while it processes a new
270     * conf file and does the job_end_push().
271     */
272    lock_jobs();
273    lock_jcr_chain();
274    if (!jcrs) {
275       jcrs = New(dlist(jcr, &jcr->link));
276    }
277    jcrs->append(jcr);
278    unlock_jcr_chain();
279    unlock_jobs();
280
281    return jcr;
282 }
283
284
285 /*
286  * Remove a JCR from the chain
287  * NOTE! The chain must be locked prior to calling
288  *       this routine.
289  */
290 static void remove_jcr(JCR *jcr)
291 {
292    Dmsg0(3400, "Enter remove_jcr\n");
293    if (!jcr) {
294       Emsg0(M_ABORT, 0, _("NULL jcr.\n"));
295    }
296    jcrs->remove(jcr);
297    Dmsg0(3400, "Leave remove_jcr\n");
298 }
299
300 /*
301  * Free stuff common to all JCRs.  N.B. Be careful to include only
302  *  generic stuff in the common part of the jcr.
303  */
304 static void free_common_jcr(JCR *jcr)
305 {
306    struct s_last_job *je, last_job;
307
308    /* Keep some statistics */
309    switch (jcr->JobType) {
310    case JT_BACKUP:
311    case JT_VERIFY:
312    case JT_RESTORE:
313    case JT_MIGRATE:
314    case JT_COPY:
315    case JT_ADMIN:
316       num_jobs_run++;
317       last_job.Errors = jcr->Errors;
318       last_job.JobType = jcr->JobType;
319       last_job.JobId = jcr->JobId;
320       last_job.VolSessionId = jcr->VolSessionId;
321       last_job.VolSessionTime = jcr->VolSessionTime;
322       bstrncpy(last_job.Job, jcr->Job, sizeof(last_job.Job));
323       last_job.JobFiles = jcr->JobFiles;
324       last_job.JobBytes = jcr->JobBytes;
325       last_job.JobStatus = jcr->JobStatus;
326       last_job.JobLevel = jcr->JobLevel;
327       last_job.start_time = jcr->start_time;
328       last_job.end_time = time(NULL);
329       /* Keep list of last jobs, but not Console where JobId==0 */
330       if (last_job.JobId > 0) {
331          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
332          memcpy((char *)je, (char *)&last_job, sizeof(last_job));
333          if (!last_jobs) {
334             init_last_jobs_list();
335          }
336          last_jobs->append(je);
337          if (last_jobs->size() > max_last_jobs) {
338             je = (struct s_last_job *)last_jobs->first();
339             last_jobs->remove(je);
340             free(je);
341          }
342       }
343       break;
344    default:
345       break;
346    }
347    jcr->destroy_mutex();
348
349    if (jcr->msg_queue) {
350       delete jcr->msg_queue;
351       jcr->msg_queue = NULL;
352    }
353    close_msg(jcr);                    /* close messages for this job */
354
355    /* do this after closing messages */
356    if (jcr->client_name) {
357       free_pool_memory(jcr->client_name);
358       jcr->client_name = NULL;
359    }
360
361    if (jcr->attr) {
362       free_pool_memory(jcr->attr);
363       jcr->attr = NULL;
364    }
365
366    if (jcr->sd_auth_key) {
367       free(jcr->sd_auth_key);
368       jcr->sd_auth_key = NULL;
369    }
370    if (jcr->VolumeName) {
371       free_pool_memory(jcr->VolumeName);
372       jcr->VolumeName = NULL;
373    }
374
375    if (jcr->dir_bsock) {
376       bnet_close(jcr->dir_bsock);
377       jcr->dir_bsock = NULL;
378    }
379    if (jcr->errmsg) {
380       free_pool_memory(jcr->errmsg);
381       jcr->errmsg = NULL;
382    }
383    if (jcr->where) {
384       free(jcr->where);
385       jcr->where = NULL;
386    }
387    if (jcr->RegexWhere) {
388       free(jcr->RegexWhere);
389       jcr->RegexWhere = NULL;
390    }
391    if (jcr->where_bregexp) {
392       free_bregexps(jcr->where_bregexp);
393       delete jcr->where_bregexp;
394       jcr->where_bregexp = NULL;
395    }
396    if (jcr->cached_path) {
397       free_pool_memory(jcr->cached_path);
398       jcr->cached_path = NULL;
399       jcr->cached_pnl = 0;
400    }
401    free_getuser_cache();
402    free_getgroup_cache();
403    free(jcr);
404 }
405
406 /*
407  * Global routine to free a jcr
408  */
409 #ifdef DEBUG
410 void b_free_jcr(const char *file, int line, JCR *jcr)
411 {
412    Dmsg3(3400, "Enter free_jcr 0x%x from %s:%d\n", jcr, file, line);
413
414 #else
415
416 void free_jcr(JCR *jcr)
417 {
418
419    Dmsg2(3400, "Enter free_jcr 0x%x job=%d\n", jcr, jcr->JobId);
420
421 #endif
422
423    dequeue_messages(jcr);
424    lock_jcr_chain();
425    jcr->dec_use_count();              /* decrement use count */
426    if (jcr->use_count() < 0) {
427       Emsg2(M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
428          jcr->use_count(), jcr->JobId);
429    }
430    Dmsg3(3400, "Dec free_jcr 0x%x use_count=%d jobid=%d\n", jcr, jcr->use_count(), jcr->JobId);
431    if (jcr->use_count() > 0) {          /* if in use */
432       unlock_jcr_chain();
433       Dmsg3(3400, "free_jcr 0x%x job=%d use_count=%d\n", jcr, jcr->JobId, jcr->use_count());
434       return;
435    }
436
437    remove_jcr(jcr);                   /* remove Jcr from chain */
438    unlock_jcr_chain();
439
440    job_end_pop(jcr);                  /* pop and call hooked routines */
441
442    Dmsg1(3400, "End job=%d\n", jcr->JobId);
443    if (jcr->daemon_free_jcr) {
444       jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
445    }
446    free_common_jcr(jcr);
447    close_msg(NULL);                   /* flush any daemon messages */
448    garbage_collect_memory_pool();
449    Dmsg0(3400, "Exit free_jcr\n");
450 }
451
452
453 /*
454  * Given a JobId, find the JCR
455  *   Returns: jcr on success
456  *            NULL on failure
457  */
458 JCR *get_jcr_by_id(uint32_t JobId)
459 {
460    JCR *jcr;
461
462    foreach_jcr(jcr) {
463       if (jcr->JobId == JobId) {
464          jcr->inc_use_count();
465          Dmsg2(3400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
466          break;
467       }
468    }
469    endeach_jcr(jcr);
470    return jcr;
471 }
472
473 /*
474  * Given a SessionId and SessionTime, find the JCR
475  *   Returns: jcr on success
476  *            NULL on failure
477  */
478 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
479 {
480    JCR *jcr;
481
482    foreach_jcr(jcr) {
483       if (jcr->VolSessionId == SessionId &&
484           jcr->VolSessionTime == SessionTime) {
485          jcr->inc_use_count();
486          Dmsg2(3400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
487          break;
488       }
489    }
490    endeach_jcr(jcr);
491    return jcr;
492 }
493
494
495 /*
496  * Given a Job, find the JCR
497  *  compares on the number of characters in Job
498  *  thus allowing partial matches.
499  *   Returns: jcr on success
500  *            NULL on failure
501  */
502 JCR *get_jcr_by_partial_name(char *Job)
503 {
504    JCR *jcr;
505    int len;
506
507    if (!Job) {
508       return NULL;
509    }
510    len = strlen(Job);
511    foreach_jcr(jcr) {
512       if (strncmp(Job, jcr->Job, len) == 0) {
513          jcr->inc_use_count();
514          Dmsg2(3400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
515          break;
516       }
517    }
518    endeach_jcr(jcr);
519    return jcr;
520 }
521
522
523 /*
524  * Given a Job, find the JCR
525  *  requires an exact match of names.
526  *   Returns: jcr on success
527  *            NULL on failure
528  */
529 JCR *get_jcr_by_full_name(char *Job)
530 {
531    JCR *jcr;
532
533    if (!Job) {
534       return NULL;
535    }
536    foreach_jcr(jcr) {
537       if (strcmp(jcr->Job, Job) == 0) {
538          jcr->inc_use_count();
539          Dmsg2(3400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
540          break;
541       }
542    }
543    endeach_jcr(jcr);
544    return jcr;
545 }
546
547 void set_jcr_job_status(JCR *jcr, int JobStatus)
548 {
549    /*
550     * For a set of errors, ... keep the current status
551     *   so it isn't lost. For all others, set it.
552     */
553    switch (jcr->JobStatus) {
554    case JS_ErrorTerminated:
555    case JS_Error:
556    case JS_FatalError:
557    case JS_Differences:
558    case JS_Canceled:
559       break;
560    default:
561       jcr->JobStatus = JobStatus;
562    }
563 }
564
565 #ifdef TRACE_JCR_CHAIN
566 static int lock_count = 0;
567 #endif
568
569 /*
570  * Lock the chain
571  */
572 #ifdef TRACE_JCR_CHAIN
573 static void b_lock_jcr_chain(const char *fname, int line)
574 #else
575 static void lock_jcr_chain()
576 #endif
577 {
578 #ifdef TRACE_JCR_CHAIN
579    Dmsg3(3400, "Lock jcr chain %d from %s:%d\n", ++lock_count,
580       fname, line);
581 #endif
582    P(jcr_lock);
583 }
584
585 /*
586  * Unlock the chain
587  */
588 #ifdef TRACE_JCR_CHAIN
589 static void b_unlock_jcr_chain(const char *fname, int line)
590 #else
591 static void unlock_jcr_chain()
592 #endif
593 {
594 #ifdef TRACE_JCR_CHAIN
595    Dmsg3(3400, "Unlock jcr chain %d from %s:%d\n", lock_count--,
596       fname, line);
597 #endif
598    V(jcr_lock);
599 }
600
601
602 /*
603  * Start walk of jcr chain
604  * The proper way to walk the jcr chain is:
605  *    JCR *jcr;
606  *    foreach_jcr(jcr) {
607  *      ...
608  *    }
609  *    endeach_jcr(jcr);
610  *
611  *  It is possible to leave out the endeach_jcr(jcr), but
612  *   in that case, the last jcr referenced must be explicitly
613  *   released with:
614  *
615  *    free_jcr(jcr);
616  *  
617  */
618 JCR *jcr_walk_start() 
619 {
620    JCR *jcr;
621    lock_jcr_chain();
622    jcr = (JCR *)jcrs->first();
623    if (jcr) {
624       jcr->inc_use_count();
625       Dmsg3(3400, "Inc jcr_walk_start 0x%x job=%d use_count=%d\n", jcr, 
626             jcr->JobId, jcr->use_count());
627    }
628    unlock_jcr_chain();
629    return jcr;
630 }
631
632 /*
633  * Get next jcr from chain, and release current one
634  */
635 JCR *jcr_walk_next(JCR *prev_jcr)
636 {
637    JCR *jcr;
638
639    lock_jcr_chain();
640    jcr = (JCR *)jcrs->next(prev_jcr);
641    if (jcr) {
642       jcr->inc_use_count();
643       Dmsg3(3400, "Inc jcr_walk_next 0x%x job=%d use_count=%d\n", jcr, 
644          jcr->JobId, jcr->use_count());
645    }
646    unlock_jcr_chain();
647    if (prev_jcr) {
648       free_jcr(prev_jcr);
649    }
650    return jcr;
651 }
652
653 /*
654  * Release last jcr referenced
655  */
656 void jcr_walk_end(JCR *jcr)
657 {
658    if (jcr) {
659       free_jcr(jcr);
660    }
661 }
662
663
664 /*
665  * Setup to call the timeout check routine every 30 seconds
666  *  This routine will check any timers that have been enabled.
667  */
668 bool init_jcr_subsystem(void)
669 {
670    watchdog_t *wd = new_watchdog();
671
672    wd->one_shot = false;
673    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
674                          if only with a #define */
675    wd->callback = jcr_timeout_check;
676
677    register_watchdog(wd);
678
679    return true;
680 }
681
682 static void jcr_timeout_check(watchdog_t *self)
683 {
684    JCR *jcr;
685    BSOCK *fd;
686    time_t timer_start;
687
688    Dmsg0(3400, "Start JCR timeout checks\n");
689
690    /* Walk through all JCRs checking if any one is
691     * blocked for more than specified max time.
692     */
693    foreach_jcr(jcr) {
694       Dmsg2(3400, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
695       if (jcr->JobId == 0) {
696          continue;
697       }
698       fd = jcr->store_bsock;
699       if (fd) {
700          timer_start = fd->timer_start;
701          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
702             fd->timer_start = 0;      /* turn off timer */
703             fd->timed_out = true;
704             Jmsg(jcr, M_ERROR, 0, _(
705 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
706                  watchdog_time - timer_start);
707             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
708          }
709       }
710       fd = jcr->file_bsock;
711       if (fd) {
712          timer_start = fd->timer_start;
713          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
714             fd->timer_start = 0;      /* turn off timer */
715             fd->timed_out = true;
716             Jmsg(jcr, M_ERROR, 0, _(
717 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
718                  watchdog_time - timer_start);
719             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
720          }
721       }
722       fd = jcr->dir_bsock;
723       if (fd) {
724          timer_start = fd->timer_start;
725          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
726             fd->timer_start = 0;      /* turn off timer */
727             fd->timed_out = true;
728             Jmsg(jcr, M_ERROR, 0, _(
729 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
730                  watchdog_time - timer_start);
731             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
732          }
733       }
734    }
735    endeach_jcr(jcr);
736
737    Dmsg0(3400, "Finished JCR timeout checks\n");
738 }
739
740 /*
741  * Timeout signal comes here
742  */
743 extern "C" void timeout_handler(int sig)
744 {
745    return;                            /* thus interrupting the function */
746 }