forked from osmocom/wireshark
Ensure tshark/wireshark always get good err msgs from dumpcap:
1. Clean up dumpcap 'as a child' err msg handling so that: - all err msgs are properly formatted when being sent back to the parent. - any log Critical, Warning, etc messages are sent back to parent and are properly formatted. 2. Change handling of -w <...> slightly in capture_opts.c so that wireshark provides a good error message if there is a 'write permissions' issue on the file. (Previously the error popup said only "Child exited with status 2"). This fixes bug #2288. Add some conditionalized DEBUG_CHILD_DUMPCAP code for dumpcap debug logging to a file. svn path=/trunk/; revision=24446
This commit is contained in:
parent
e6837f14d4
commit
8d4f01eea7
|
@ -911,10 +911,9 @@ static gboolean capture_opts_output_to_pipe(const char *save_file, gboolean *is_
|
||||||
*is_pipe = TRUE;
|
*is_pipe = TRUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: /* couldn't stat it */
|
default: /* couldn't stat it */
|
||||||
cmdarg_err("Error testing whether capture file is a pipe: %s",
|
break; /* ignore: later attempt to open */
|
||||||
strerror(errno));
|
/* will generate a nice msg */
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,6 +291,8 @@ sync_pipe_start(capture_options *capture_opts) {
|
||||||
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "We don't know where to find dumpcap.");
|
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "We don't know where to find dumpcap.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "argv[0]: %s", argv[0]);
|
||||||
|
|
||||||
argv = sync_pipe_add_arg(argv, &argc, "-i");
|
argv = sync_pipe_add_arg(argv, &argc, "-i");
|
||||||
argv = sync_pipe_add_arg(argv, &argc, capture_opts->iface);
|
argv = sync_pipe_add_arg(argv, &argc, capture_opts->iface);
|
||||||
|
@ -1105,6 +1107,7 @@ pipe_read_block(int pipe, char *indicator, int len, char *msg) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXX If message is "2part", the msg probably won't be sent to debug log correctly */
|
||||||
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
|
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
|
||||||
"read %d ok indicator: %c len: %u msg: %s", pipe, *indicator,
|
"read %d ok indicator: %c len: %u msg: %s", pipe, *indicator,
|
||||||
len, msg);
|
len, msg);
|
||||||
|
|
146
dumpcap.c
146
dumpcap.c
|
@ -107,6 +107,13 @@
|
||||||
#include "wiretap/libpcap.h"
|
#include "wiretap/libpcap.h"
|
||||||
|
|
||||||
/*#define DEBUG_DUMPCAP*/
|
/*#define DEBUG_DUMPCAP*/
|
||||||
|
/*#define DEBUG_CHILD_DUMPCAP*/
|
||||||
|
|
||||||
|
#ifdef DEBUG_CHILD_DUMPCAP
|
||||||
|
FILE *debug_log; /* for logging debug messages to */
|
||||||
|
/* a file if DEBUG_CHILD_DUMPCAP */
|
||||||
|
/* is defined */
|
||||||
|
#endif
|
||||||
|
|
||||||
static gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */
|
static gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -353,9 +360,12 @@ cmdarg_err(const char *fmt, ...)
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
if(capture_child) {
|
if(capture_child) {
|
||||||
/* Print a bare error */
|
gchar *msg;
|
||||||
|
/* Generate a 'special format' message back to parent */
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vfprintf(stderr, fmt, ap);
|
msg = g_strdup_vprintf(fmt, ap);
|
||||||
|
sync_pipe_errmsg_to_parent(2, msg, "");
|
||||||
|
g_free(msg);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
} else {
|
} else {
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
|
@ -375,7 +385,12 @@ cmdarg_err_cont(const char *fmt, ...)
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
if(capture_child) {
|
if(capture_child) {
|
||||||
/* XXX - convert to g_log */
|
gchar *msg;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
msg = g_strdup_vprintf(fmt, ap);
|
||||||
|
sync_pipe_errmsg_to_parent(2, msg, "");
|
||||||
|
g_free(msg);
|
||||||
|
va_end(ap);
|
||||||
} else {
|
} else {
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vfprintf(stderr, fmt, ap);
|
vfprintf(stderr, fmt, ap);
|
||||||
|
@ -483,14 +498,14 @@ relinquish_privs_except_capture(void)
|
||||||
if (started_with_special_privs()) {
|
if (started_with_special_privs()) {
|
||||||
print_caps("Pre drop, pre set");
|
print_caps("Pre drop, pre set");
|
||||||
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
|
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
|
||||||
perror("prctl()");
|
cmdarg_err("prctl() fail return: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
cap_set_flag(caps, CAP_PERMITTED, cl_len, cap_list, CAP_SET);
|
cap_set_flag(caps, CAP_PERMITTED, cl_len, cap_list, CAP_SET);
|
||||||
cap_set_flag(caps, CAP_INHERITABLE, cl_len, cap_list, CAP_SET);
|
cap_set_flag(caps, CAP_INHERITABLE, cl_len, cap_list, CAP_SET);
|
||||||
|
|
||||||
if (cap_set_proc(caps)) {
|
if (cap_set_proc(caps)) {
|
||||||
perror("capset()");
|
cmdarg_err("cap_set_proc() fail return: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
print_caps("Pre drop, post set");
|
print_caps("Pre drop, post set");
|
||||||
}
|
}
|
||||||
|
@ -500,7 +515,7 @@ relinquish_privs_except_capture(void)
|
||||||
print_caps("Post drop, pre set");
|
print_caps("Post drop, pre set");
|
||||||
cap_set_flag(caps, CAP_EFFECTIVE, cl_len, cap_list, CAP_SET);
|
cap_set_flag(caps, CAP_EFFECTIVE, cl_len, cap_list, CAP_SET);
|
||||||
if (cap_set_proc(caps)) {
|
if (cap_set_proc(caps)) {
|
||||||
perror("capset()");
|
cmdarg_err("cap_set_proc() fail return: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
print_caps("Post drop, post set");
|
print_caps("Post drop, post set");
|
||||||
cap_free(caps);
|
cap_free(caps);
|
||||||
|
@ -2198,6 +2213,7 @@ main(int argc, char *argv[])
|
||||||
gboolean machine_readable = FALSE;
|
gboolean machine_readable = FALSE;
|
||||||
gboolean print_statistics = FALSE;
|
gboolean print_statistics = FALSE;
|
||||||
int status, run_once_args = 0;
|
int status, run_once_args = 0;
|
||||||
|
gint i;
|
||||||
|
|
||||||
#ifdef HAVE_PCAP_REMOTE
|
#ifdef HAVE_PCAP_REMOTE
|
||||||
#define OPTSTRING_INIT "a:A:b:c:Df:hi:Lm:MprSs:uvw:y:Z:"
|
#define OPTSTRING_INIT "a:A:b:c:Df:hi:Lm:MprSs:uvw:y:Z:"
|
||||||
|
@ -2214,6 +2230,61 @@ main(int argc, char *argv[])
|
||||||
char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_WIN32) - 1] =
|
char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_WIN32) - 1] =
|
||||||
OPTSTRING_INIT OPTSTRING_WIN32;
|
OPTSTRING_INIT OPTSTRING_WIN32;
|
||||||
|
|
||||||
|
#ifdef DEBUG_CHILD_DUMPCAP
|
||||||
|
if ((debug_log = fopen("dumpcap_debug_log.tmp","w")) == NULL) {
|
||||||
|
fprintf (stderr, "Unable to open debug log file !\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Determine if dumpcap is running as a child process by going thru */
|
||||||
|
/* the command line args to see if a -Z option (which is the */
|
||||||
|
/* hidden option which indicates that dumpcap has been invoked */
|
||||||
|
/* from a parent process using a pipe). */
|
||||||
|
/* "Child" status needs to be determined immediately upon startup so */
|
||||||
|
/* that any messages generated by dumpcap (eg: during initialization)*/
|
||||||
|
/* will be formatted properly if being sent back to a parent via */
|
||||||
|
/* a pipe. */
|
||||||
|
|
||||||
|
for (i=1; i<argc; i++) {
|
||||||
|
if (g_ascii_strcasecmp("-Z", argv[i]) == 0) {
|
||||||
|
capture_child = TRUE;
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* set output pipe to binary mode, to avoid ugly text conversions */
|
||||||
|
_setmode(2, O_BINARY);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The default_log_handler will use stdout, which makes trouble in */
|
||||||
|
/* capture child mode, as it uses stdout for it's sync_pipe. */
|
||||||
|
/* So: the filtering is done in the console_log_handler and not here.*/
|
||||||
|
/* We set the log handlers right up front to make sure that any log */
|
||||||
|
/* messages when running as child will be sent back to the parent */
|
||||||
|
/* with the correct format. */
|
||||||
|
|
||||||
|
log_flags =
|
||||||
|
G_LOG_LEVEL_ERROR|
|
||||||
|
G_LOG_LEVEL_CRITICAL|
|
||||||
|
G_LOG_LEVEL_WARNING|
|
||||||
|
G_LOG_LEVEL_MESSAGE|
|
||||||
|
G_LOG_LEVEL_INFO|
|
||||||
|
G_LOG_LEVEL_DEBUG|
|
||||||
|
G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION;
|
||||||
|
|
||||||
|
g_log_set_handler(NULL,
|
||||||
|
log_flags,
|
||||||
|
console_log_handler, NULL /* user_data */);
|
||||||
|
g_log_set_handler(LOG_DOMAIN_MAIN,
|
||||||
|
log_flags,
|
||||||
|
console_log_handler, NULL /* user_data */);
|
||||||
|
g_log_set_handler(LOG_DOMAIN_CAPTURE,
|
||||||
|
log_flags,
|
||||||
|
console_log_handler, NULL /* user_data */);
|
||||||
|
g_log_set_handler(LOG_DOMAIN_CAPTURE_CHILD,
|
||||||
|
log_flags,
|
||||||
|
console_log_handler, NULL /* user_data */);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
/* Load wpcap if possible. Do this before collecting the run-time version information */
|
/* Load wpcap if possible. Do this before collecting the run-time version information */
|
||||||
load_wpcap();
|
load_wpcap();
|
||||||
|
@ -2245,31 +2316,6 @@ main(int argc, char *argv[])
|
||||||
relinquish_privs_except_capture();
|
relinquish_privs_except_capture();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* the default_log_handler will use stdout, which makes trouble in */
|
|
||||||
/* capture child mode, as it uses stdout for it's sync_pipe */
|
|
||||||
/* so do the filtering in the console_log_handler and not here */
|
|
||||||
log_flags =
|
|
||||||
G_LOG_LEVEL_ERROR|
|
|
||||||
G_LOG_LEVEL_CRITICAL|
|
|
||||||
G_LOG_LEVEL_WARNING|
|
|
||||||
G_LOG_LEVEL_MESSAGE|
|
|
||||||
G_LOG_LEVEL_INFO|
|
|
||||||
G_LOG_LEVEL_DEBUG|
|
|
||||||
G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION;
|
|
||||||
|
|
||||||
g_log_set_handler(NULL,
|
|
||||||
log_flags,
|
|
||||||
console_log_handler, NULL /* user_data */);
|
|
||||||
g_log_set_handler(LOG_DOMAIN_MAIN,
|
|
||||||
log_flags,
|
|
||||||
console_log_handler, NULL /* user_data */);
|
|
||||||
g_log_set_handler(LOG_DOMAIN_CAPTURE,
|
|
||||||
log_flags,
|
|
||||||
console_log_handler, NULL /* user_data */);
|
|
||||||
g_log_set_handler(LOG_DOMAIN_CAPTURE_CHILD,
|
|
||||||
log_flags,
|
|
||||||
console_log_handler, NULL /* user_data */);
|
|
||||||
|
|
||||||
/* Set the initial values in the capture_opts. This might be overwritten
|
/* Set the initial values in the capture_opts. This might be overwritten
|
||||||
by the command line parameters. */
|
by the command line parameters. */
|
||||||
capture_opts_init(capture_opts, NULL);
|
capture_opts_init(capture_opts, NULL);
|
||||||
|
@ -2475,13 +2521,13 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level,
|
||||||
const char *message, gpointer user_data _U_)
|
const char *message, gpointer user_data _U_)
|
||||||
{
|
{
|
||||||
time_t curr;
|
time_t curr;
|
||||||
struct tm *today;
|
struct tm *today;
|
||||||
const char *level;
|
const char *level;
|
||||||
|
gchar *msg;
|
||||||
|
|
||||||
/* ignore log message, if log_level isn't interesting */
|
/* ignore log message, if log_level isn't interesting */
|
||||||
if( !(log_level & G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_DEBUG|G_LOG_LEVEL_INFO))) {
|
if( !(log_level & G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_DEBUG|G_LOG_LEVEL_INFO))) {
|
||||||
#ifndef DEBUG_DUMPCAP
|
#if !defined(DEBUG_DUMPCAP) && !defined(DEBUG_CHILD_DUMPCAP)
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -2515,19 +2561,43 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level,
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* don't use printf (stdout), in child mode we're using stdout for the sync_pipe */
|
/* Generate the output message */
|
||||||
if(log_level & G_LOG_LEVEL_MESSAGE) {
|
if(log_level & G_LOG_LEVEL_MESSAGE) {
|
||||||
/* normal user messages without additional infos */
|
/* normal user messages without additional infos */
|
||||||
fprintf(stderr, "%s\n", message);
|
msg = g_strdup_printf("%s\n", message);
|
||||||
fflush(stderr);
|
|
||||||
} else {
|
} else {
|
||||||
/* info/debug messages with additional infos */
|
/* info/debug messages with additional infos */
|
||||||
fprintf(stderr, "%02u:%02u:%02u %8s %s %s\n",
|
msg = g_strdup_printf("%02u:%02u:%02u %8s %s %s\n",
|
||||||
today->tm_hour, today->tm_min, today->tm_sec,
|
today->tm_hour, today->tm_min, today->tm_sec,
|
||||||
log_domain != NULL ? log_domain : "",
|
log_domain != NULL ? log_domain : "",
|
||||||
level, message);
|
level, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEBUG & INFO msgs (if we're debugging today) */
|
||||||
|
#if defined(DEBUG_DUMPCAP) || defined(DEBUG_CHILD_DUMPCAP)
|
||||||
|
if( !(log_level & G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_DEBUG|G_LOG_LEVEL_INFO))) {
|
||||||
|
#ifdef DEBUG_DUMPCAP
|
||||||
|
fprintf(stderr, "%s", msg);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
#ifdef DEBUG_CHILD_DUMPCAP
|
||||||
|
fprintf(debug_log, "%s", msg);
|
||||||
|
fflush(debug_log);
|
||||||
|
#endif
|
||||||
|
g_free(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ERROR, CRITICAL, WARNING, MESSAGE messages goto stderr or */
|
||||||
|
/* to parent especially formatted if dumpcap running as child. */
|
||||||
|
if (capture_child) {
|
||||||
|
sync_pipe_errmsg_to_parent(2, msg, "");
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%s", msg);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
|
g_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue