From 4c84892cead59e568a83172e441a15713ae162ab Mon Sep 17 00:00:00 2001 From: Wenji Huang Date: Mon, 19 Oct 2009 13:49:23 -0400 Subject: fix 32-bit compatibility for @hist_log printing * stat-common.c (_stp_stat_print_histogram_buf): Fix HIST_PRINTF parameter passing. --- runtime/stat-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/stat-common.c b/runtime/stat-common.c index e6fd3a11..7123dc8e 100644 --- a/runtime/stat-common.c +++ b/runtime/stat-common.c @@ -270,7 +270,7 @@ static void _stp_stat_print_histogram_buf(char *buf, size_t size, Hist st, stat for (j = 0; j < v; ++j) HIST_PRINTF("@"); - HIST_PRINTF("%*lld\n", HIST_WIDTH - v + 1 + cnt_space, sd->histogram[i]); + HIST_PRINTF("%*lld\n", (int)(HIST_WIDTH - v + 1 + cnt_space), sd->histogram[i]); } HIST_PRINTF("\n"); #undef HIST_PRINTF -- cgit From 892e5de27d2f275e2d7c2160d81fe9e0c87b1ee1 Mon Sep 17 00:00:00 2001 From: "Frank Ch. Eigler" Date: Mon, 19 Oct 2009 13:53:33 -0400 Subject: bemoan that _stp_*printf can't be protected with gcc attribute printf --- runtime/string.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/string.c b/runtime/string.c index cdafbf64..b3f81f0e 100644 --- a/runtime/string.c +++ b/runtime/string.c @@ -21,7 +21,11 @@ */ /** Sprintf into a string. - * Like printf, except output goes into a string. + * Like printf, except output goes into a string. + * + * NB: these are script language printf formatting directives, where + * %d ints are 64-bits etc, so we can't use gcc level attribute printf + * to type-check the arguments. * * @param str string * @param fmt A printf-style format string followed by a -- cgit From 81fb86d83da9964e7d9e2330d78b2008f18054e4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 19 Oct 2009 18:11:58 -0700 Subject: Improve some runtime struct layouts Guided by pahole, I've shaved off a few padding bytes here and there. New sizes on x86_64: stap_task_finder_target 192 -> 184 stap_itrace_probe 216 -> 208 stap_utrace_probe 328 -> 312 stap_uprobe_tf 200 -> 192 stap_uprobe_spec 48 -> 40 I only changed field layouts, not types or names, so this should be perfectly safe. (FLW) --- runtime/task_finder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/task_finder.c b/runtime/task_finder.c index 6b50f1b9..e89ac8ee 100644 --- a/runtime/task_finder.c +++ b/runtime/task_finder.c @@ -87,15 +87,15 @@ struct stap_task_finder_target { struct list_head callback_list_head; struct list_head callback_list; struct utrace_engine_ops ops; + size_t pathlen; unsigned engine_attached:1; unsigned mmap_events:1; unsigned munmap_events:1; unsigned mprotect_events:1; - size_t pathlen; /* public: */ - const char *procname; pid_t pid; + const char *procname; stap_task_finder_callback callback; stap_task_finder_mmap_callback mmap_callback; stap_task_finder_munmap_callback munmap_callback; -- cgit From bf043a5f9c9f807d670276b6c389bf5439245edb Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Tue, 20 Oct 2009 13:55:15 +0200 Subject: Be paranoid about table size resolving cie_for_fde and fde_pointer_type. * runtime/unwind.c (cie_for_fde): Take table and table_len into account. (fde_pointer_type): Likewise. * runtime/unwind/unwind.h: Adjust function prototypes. --- runtime/unwind.c | 25 ++++++++++++++++++------- runtime/unwind/unwind.h | 6 ++++-- 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/unwind.c b/runtime/unwind.c index 00108a39..0b4e6a9e 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -88,7 +88,7 @@ static sleb128_t get_sleb128(const u8 **pcur, const u8 *end) /* given an FDE, find its CIE */ static const u32 *cie_for_fde(const u32 *fde, void *unwind_data, - int is_ehframe) + uint32_t table_len, int is_ehframe) { const u32 *cie; @@ -118,6 +118,11 @@ static const u32 *cie_for_fde(const u32 *fde, void *unwind_data, else cie = unwind_data + fde[1]; + /* Make sure address falls in the table */ + if (((void *)cie) < ((void*)unwind_data) + || ((void*)cie) > ((void*)(unwind_data + table_len))) + return NULL; + if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde) || (*cie & (sizeof(*cie) - 1)) || (cie[1] != 0xffffffff && cie[1] != 0)) { @@ -200,7 +205,8 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrTy return value; } -static signed fde_pointer_type(const u32 *cie) +static signed fde_pointer_type(const u32 *cie, void *unwind_data, + uint32_t table_len) { const u8 *ptr = (const u8 *)(cie + 2); unsigned version = *ptr; @@ -212,11 +218,16 @@ static signed fde_pointer_type(const u32 *cie) const u8 *end = (const u8 *)(cie + 1) + *cie; uleb128_t len; + /* end of cie should fall within unwind table. */ + if (((void*)end) < ((void *)unwind_data) + || ((void *)end) > ((void *)(unwind_data + table_len))) + return -1; + /* check if augmentation size is first (and thus present) */ if (*ptr != 'z') return -1; /* check if augmentation string is nul-terminated */ - if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) + if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) return -1; ++ptr; /* skip terminator */ get_uleb128(&ptr, end); /* skip code alignment */ @@ -606,10 +617,10 @@ static int unwind_frame(struct unwind_frame_info *frame, /* found the fde, now set startLoc and endLoc */ if (fde != NULL) { - cie = cie_for_fde(fde, table, is_ehframe); + cie = cie_for_fde(fde, table, table_len, is_ehframe); if (likely(cie != NULL && cie != &bad_cie && cie != ¬_fde)) { ptr = (const u8 *)(fde + 2); - ptrType = fde_pointer_type(cie); + ptrType = fde_pointer_type(cie, table, table_len); startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); startLoc = adjustStartLoc(startLoc, m, s, ptrType, is_ehframe); @@ -632,12 +643,12 @@ static int unwind_frame(struct unwind_frame_info *frame, for (fde = table, tableSize = table_len; cie = NULL, tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { dbug_unwind(3, "fde=%lx tableSize=%d\n", (long)*fde, (int)tableSize); - cie = cie_for_fde(fde, table, is_ehframe); + cie = cie_for_fde(fde, table, table_len, is_ehframe); if (cie == &bad_cie) { cie = NULL; break; } - if (cie == NULL || cie == ¬_fde || (ptrType = fde_pointer_type(cie)) < 0) + if (cie == NULL || cie == ¬_fde || (ptrType = fde_pointer_type(cie, table, table_len)) < 0) continue; ptr = (const u8 *)(fde + 2); diff --git a/runtime/unwind/unwind.h b/runtime/unwind/unwind.h index 285a3a34..023ea603 100644 --- a/runtime/unwind/unwind.h +++ b/runtime/unwind/unwind.h @@ -143,8 +143,10 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrType); static const u32 bad_cie, not_fde; -static const u32 *cie_for_fde(const u32 *fde, void *table, int is_ehframe); -static signed fde_pointer_type(const u32 *cie); +static const u32 *cie_for_fde(const u32 *fde, void *table, + uint32_t table_len, int is_ehframe); +static signed fde_pointer_type(const u32 *cie, + void *table, uint32_t table_len); #endif /* STP_USE_DWARF_UNWINDER */ -- cgit From 1adb61a4e1313b178f2db7d5ce766a505c073a24 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Tue, 20 Oct 2009 16:55:04 +0200 Subject: Make sure cie and fde end point to sane values in while doing unwind_frame. * runtime/unwind.c (unwind_frame): Check end read from cie or fde doesn't go passed end of unwind table. --- runtime/unwind.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'runtime') diff --git a/runtime/unwind.c b/runtime/unwind.c index 0b4e6a9e..0e95ba08 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -677,6 +677,12 @@ static int unwind_frame(struct unwind_frame_info *frame, state.cieEnd = ptr; /* keep here temporarily */ ptr = (const u8 *)(cie + 2); end = (const u8 *)(cie + 1) + *cie; + + /* end should fall within unwind table. */ + if (((void *)end) < table + || ((void *)end) > ((void *)(table + table_len))) + goto err; + frame->call_frame = 1; if ((state.version = *ptr) != 1) { dbug_unwind(1, "CIE version number is %d. 1 is supported.\n", state.version); @@ -734,6 +740,11 @@ static int unwind_frame(struct unwind_frame_info *frame, state.cieEnd = end; end = (const u8 *)(fde + 1) + *fde; + /* end should fall within unwind table. */ + if (((void*)end) < table + || ((void *)end) > ((void *)(table + table_len))) + goto err; + /* skip augmentation */ if (((const char *)(cie + 2))[1] == 'z') { uleb128_t augSize = get_uleb128(&ptr, end); -- cgit From bc0b26aa958253192328bc4084ba367536fb4842 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Tue, 20 Oct 2009 17:08:57 +0200 Subject: Limit the number of call frame instructions we process in the unwinder. * runtime/unwind.c (processCFI): Fail if the number of instructions is larger than MAX_CFI (currently 512). --- runtime/unwind.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime') diff --git a/runtime/unwind.c b/runtime/unwind.c index 0e95ba08..7607770e 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -278,6 +278,10 @@ static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value, s } } +/* Limit the number of instructions we process. Arbitrary limit. + 512 should be enough for anybody... */ +#define MAX_CFI 512 + static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, signed ptrType, struct unwind_state *state) { union { @@ -287,6 +291,9 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, s } ptr; int result = 1; + if (end - start > MAX_CFI) + return 0; + dbug_unwind(1, "targetLoc=%lx state->loc=%lx\n", targetLoc, state->loc); if (start != state->cieStart) { state->loc = state->org; -- cgit From 29bb0bbc8603edb20de09e79fd8addb4a174947d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 21 Oct 2009 16:15:58 -0700 Subject: Refactor probe locking into shared functions For scripts with thousands of probes, we save a fair amount of code-gen time in pass-4 by having the common locking code extracted into shared functions. * runtime/probe_lock.h (stp_lock_probe, stp_unlock_probe): New. * translate.cxx (c_unparser::emit_lock_decls): New, emits a static const array of locks needed for each probe. (c_unparser::emit_locks): Just call stp_lock_probe. (c_unparser::emit_unlocks): Just call stp_unlock_probe. --- runtime/probe_lock.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 runtime/probe_lock.h (limited to 'runtime') diff --git a/runtime/probe_lock.h b/runtime/probe_lock.h new file mode 100644 index 00000000..1915d4ff --- /dev/null +++ b/runtime/probe_lock.h @@ -0,0 +1,68 @@ +/* probe locking header file + * Copyright (C) 2009 Red Hat Inc. + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + */ + +#ifndef _PROBE_LOCK_H +#define _PROBE_LOCK_H + +#include + +// XXX: old 2.6 kernel hack +#ifndef read_trylock +#define read_trylock(x) ({ read_lock(x); 1; }) +#endif + +struct stp_probe_lock { + #ifdef STP_TIMING + atomic_t *skipped; + #endif + rwlock_t *lock; + unsigned write_p; +}; + + +static void +stp_unlock_probe(const struct stp_probe_lock *locks, unsigned num_locks) +{ + unsigned i; + for (i = num_locks; i-- > 0;) { + if (locks[i].write_p) + write_unlock(locks[i].lock); + else + read_unlock(locks[i].lock); + } +} + + +static unsigned +stp_lock_probe(const struct stp_probe_lock *locks, unsigned num_locks) +{ + unsigned i, numtrylock = 0; + for (i = 0; i < num_locks; ++i) { + if (locks[i].write_p) + while (!write_trylock(locks[i].lock) && + (++numtrylock < MAXTRYLOCK)) + ndelay (TRYLOCKDELAY); + else + while (!read_trylock(locks[i].lock) && + (++numtrylock < MAXTRYLOCK)) + ndelay (TRYLOCKDELAY); + if (unlikely(numtrylock >= MAXTRYLOCK)) { + atomic_inc(&skipped_count); + #ifdef STP_TIMING + atomic_inc(locks[i].skipped); + #endif + stp_unlock_probe(locks, i); + return 0; + } + } + return 1; +} + + +#endif /* _PROBE_LOCK_H */ -- cgit From a1995fef7f86ee6d1c3860cfb7e2652d986e4aa9 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Oct 2009 12:15:29 -0700 Subject: PR10854: Use a mutex around transport startup/shutdown We had a race where the probe setup could be called during/after the probe shutdown in abnormal circumstances, which leads to kernel callbacks still registered after module unload. (BOOM) Now the setup/shutdown activities and related flags are guarded by a mutex, so we should have strict ordering. * runtime/transport/transport.c (_stp_transport_mutex): New. (_stp_handle_start): Grab the mutex, and make sure we're not exiting. (_stp_cleanup_and_exit): Grab the mutex. (_stp_lock_inode, _stp_unlock_inode): Use kernel version for checking inode locking type. --- runtime/transport/transport.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'runtime') diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c index f5efce9f..bab5efa9 100644 --- a/runtime/transport/transport.c +++ b/runtime/transport/transport.c @@ -19,6 +19,7 @@ #include #include #include +#include static int _stp_exit_flag = 0; @@ -30,6 +31,9 @@ static int _stp_ctl_attached = 0; static pid_t _stp_target = 0; static int _stp_probes_started = 0; +static int _stp_exit_called = 0; +static DEFINE_MUTEX(_stp_transport_mutex); + // For now, disable transport version 3 (unless STP_USE_RING_BUFFER is // defined). @@ -81,22 +85,26 @@ static struct workqueue_struct *_stp_wq; static void _stp_handle_start(struct _stp_msg_start *st) { - dbug_trans(1, "stp_handle_start\n"); + mutex_lock(&_stp_transport_mutex); + if (!_stp_exit_called) { + dbug_trans(1, "stp_handle_start\n"); #ifdef STAPCONF_VM_AREA - { /* PR9740: workaround for kernel valloc bug. */ - void *dummy; - dummy = alloc_vm_area (PAGE_SIZE); - free_vm_area (dummy); - } + { /* PR9740: workaround for kernel valloc bug. */ + void *dummy; + dummy = alloc_vm_area (PAGE_SIZE); + free_vm_area (dummy); + } #endif - _stp_target = st->target; - st->res = probe_start(); - if (st->res >= 0) - _stp_probes_started = 1; + _stp_target = st->target; + st->res = probe_start(); + if (st->res >= 0) + _stp_probes_started = 1; - _stp_ctl_send(STP_START, st, sizeof(*st)); + _stp_ctl_send(STP_START, st, sizeof(*st)); + } + mutex_unlock(&_stp_transport_mutex); } /* common cleanup code. */ @@ -106,8 +114,7 @@ static void _stp_handle_start(struct _stp_msg_start *st) /* when someone does /sbin/rmmod on a loaded systemtap module. */ static void _stp_cleanup_and_exit(int send_exit) { - static int _stp_exit_called = 0; - + mutex_lock(&_stp_transport_mutex); if (!_stp_exit_called) { int failures; @@ -135,6 +142,7 @@ static void _stp_cleanup_and_exit(int send_exit) _stp_ctl_send(STP_EXIT, NULL, 0); dbug_trans(1, "done with ctl_send STP_EXIT\n"); } + mutex_unlock(&_stp_transport_mutex); } static void _stp_request_exit(void) @@ -293,7 +301,7 @@ err0: static inline void _stp_lock_inode(struct inode *inode) { -#ifdef DEFINE_MUTEX +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) mutex_lock(&inode->i_mutex); #else down(&inode->i_sem); @@ -302,7 +310,7 @@ static inline void _stp_lock_inode(struct inode *inode) static inline void _stp_unlock_inode(struct inode *inode) { -#ifdef DEFINE_MUTEX +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) mutex_unlock(&inode->i_mutex); #else up(&inode->i_sem); -- cgit From d855fc1db953a049b953d0c987f93a252232e6f2 Mon Sep 17 00:00:00 2001 From: David Smith Date: Thu, 29 Oct 2009 15:54:58 -0500 Subject: Removed extra declarations. * runtime/transport/transport.h: Removed extra declarations of _stp_transport_init() and _stp_transport_close(). --- runtime/transport/transport.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'runtime') diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h index 871e37b3..ddd85495 100644 --- a/runtime/transport/transport.h +++ b/runtime/transport/transport.h @@ -13,9 +13,6 @@ static int _stp_ctl_write(int type, void *data, unsigned len); -static int _stp_transport_init(void); -static void _stp_transport_close(void); - /* STP_CTL_BUFFER_SIZE is the maximum size of a message */ /* exchanged on the control channel. */ #if STP_TRANSPORT_VERSION == 1 -- cgit From 249534c041971db5e9f89cb11b6d38d311e91f57 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 3 Nov 2009 13:38:32 -0200 Subject: Fix for bug 10866 (exit with rc != 0 on script ERRORs). This patch just make the RC=1 when any output line starts with ERROR:. Also some minors error that was returning 0 instead of 1 were fixed. --- runtime/staprun/mainloop.c | 24 ++++++++++++++---------- runtime/staprun/staprun.h | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'runtime') diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index cf8bef9a..67fbfad6 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -38,7 +38,7 @@ static void *signal_thread(void *arg) } dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); if (signum == SIGQUIT) - cleanup_and_exit(1); + cleanup_and_exit(1, 0); else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) { // send STP_EXIT rc = write(control_channel, &btype, sizeof(btype)); @@ -383,7 +383,7 @@ int init_stapio(void) /* cleanup_and_exit() closed channels, frees memory, * removes the module (if necessary) and exits. */ -void cleanup_and_exit(int detach) +void cleanup_and_exit(int detach, int rc) { static int exiting = 0; const char *staprun; @@ -467,7 +467,7 @@ void cleanup_and_exit(int detach) } if (WIFEXITED(rstatus)) { - _exit(WEXITSTATUS(rstatus)); /* only possibility for rc=0 exit */ + _exit(rc ?: WEXITSTATUS(rstatus)); } _exit(-1); } @@ -484,6 +484,7 @@ int stp_main_loop(void) uint32_t type; FILE *ofp = stdout; char recvbuf[8196]; + int error_detected = 0; setvbuf(ofp, (char *)NULL, _IOLBF, 0); setup_main_signals(); @@ -511,18 +512,21 @@ int stp_main_loop(void) case STP_REALTIME_DATA: if (write_realtime_data(data, nb)) { _perr("write error (nb=%ld)", (long)nb); - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } break; #endif case STP_OOB_DATA: eprintf("%s", (char *)data); + if (strncmp(data, "ERROR:", 5) == 0){ + error_detected = 1; + } break; case STP_EXIT: { /* module asks us to unload it and exit */ dbug(2, "got STP_EXIT\n"); - cleanup_and_exit(0); + cleanup_and_exit(0, error_detected); break; } case STP_REQUEST_EXIT: @@ -540,7 +544,7 @@ int stp_main_loop(void) if (t->res < 0) { if (target_cmd) kill(target_pid, SIGKILL); - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } else if (target_cmd) { dbug(1, "detaching pid %d\n", target_pid); #if WORKAROUND_BZ467568 @@ -555,7 +559,7 @@ int stp_main_loop(void) perror ("ptrace detach"); if (target_cmd) kill(target_pid, SIGKILL); - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } #endif } @@ -573,15 +577,15 @@ int stp_main_loop(void) struct _stp_msg_start ts; if (use_old_transport) { if (init_oldrelayfs() < 0) - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } else { if (init_relayfs() < 0) - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } ts.target = target_pid; send_request(STP_START, &ts, sizeof(ts)); if (load_only) - cleanup_and_exit(1); + cleanup_and_exit(1, 0); break; } default: diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index f9f01003..9cdbc861 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -114,7 +114,7 @@ int init_staprun(void); int init_stapio(void); int stp_main_loop(void); int send_request(int type, void *data, int len); -void cleanup_and_exit (int); +void cleanup_and_exit (int, int); int init_ctl_channel(const char *name, int verb); void close_ctl_channel(void); int init_relayfs(void); -- cgit From 89651893a8ec51ee4d77ddfd57019e350ad7b169 Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 3 Nov 2009 11:04:35 -0600 Subject: PR 10706 fixed by switching to unbuffered output. * runtime/staprun/mainloop.c (stp_main_loop): Switched to unbuffered output (instead of line buffered output). --- runtime/staprun/mainloop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index 67fbfad6..ab228937 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -486,7 +486,7 @@ int stp_main_loop(void) char recvbuf[8196]; int error_detected = 0; - setvbuf(ofp, (char *)NULL, _IOLBF, 0); + setvbuf(ofp, (char *)NULL, _IONBF, 0); setup_main_signals(); dbug(2, "in main loop\n"); -- cgit