split some parts of the packet counting functions into their own files capture_info...
[metze/wireshark/wip.git] / dumpcap.c
1 /* dumpcap.c
2  *
3  * $Id$
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@ethereal.com>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <glib.h>
29
30 #include <string.h>
31 #include <ctype.h>
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #ifdef NEED_GETOPT_H
38 #include "getopt.h"
39 #endif
40
41 #ifdef _WIN32 /* Needed for console I/O */
42 #include <conio.h>
43 #endif
44
45 #include "simple_dialog.h"
46 #include "ringbuffer.h"
47 #include "util.h"
48 #include "clopts_common.h"
49 #include "cmdarg_err.h"
50 #include "version_info.h"
51
52 #include <pcap.h>
53 #include "pcap-util.h"
54
55 #include "capture.h"
56 #include "capture_loop.h"
57 #include "capture_info.h"
58
59 #ifdef _WIN32
60 #include "capture-wpcap.h"
61 #include "capture_wpcap_packet.h"
62 #endif
63
64 #include "log.h"
65 #include "file_util.h"
66
67
68
69 GString *comp_info_str, *runtime_info_str;
70 gchar       *ethereal_path = NULL;
71
72 #ifdef _WIN32
73 static gboolean has_console = TRUE;     /* TRUE if app has console */
74 static void create_console(void);
75 static void destroy_console(void);
76 #endif
77 static void
78 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
79                     const char *message, gpointer user_data _U_);
80
81 capture_options global_capture_opts;
82 capture_options *capture_opts = &global_capture_opts;
83
84
85
86 static void
87 print_usage(gboolean print_ver) {
88
89   FILE *output;
90
91 #ifdef _WIN32
92   create_console();
93 #endif
94
95   if (print_ver) {
96     output = stdout;
97     fprintf(output, "This is dumpcap " VERSION "%s"
98         "\n (C) 1998-2005 Gerald Combs <gerald@ethereal.com>"
99         "\n\n%s\n\n%s\n",
100         svnversion, comp_info_str->str, runtime_info_str->str);
101   } else {
102     output = stderr;
103   }
104   fprintf(output, "\n%s [ -vh ] [ -Lp ] [ -a <capture autostop condition> ] ...\n", PACKAGE);     
105   fprintf(output, "\t[ -b <capture ring buffer option> ] ...\n");
106 #ifdef _WIN32
107   fprintf(output, "\t[ -B <capture buffer size> ]\n");
108 #endif
109   fprintf(output, "\t[ -c <capture packet count> ] [ -f <capture filter> ]\n");
110   fprintf(output, "\t[ -i <capture interface> ]\n");
111   fprintf(output, "\t[ -s <capture snaplen> ]\n");
112   fprintf(output, "\t[ -w <savefile> ] [ -y <capture link type> ]\n");
113 }
114
115 static void
116 show_version(void)
117 {
118   printf("dumpcap " VERSION "%s\n\n%s\n\n%s\n",
119       svnversion, comp_info_str->str, runtime_info_str->str);
120 }
121
122 /*
123  * Report an error in command-line arguments.
124  * Creates a console on Windows.
125  * XXX - pop this up in a window of some sort on UNIX+X11 if the controlling
126  * terminal isn't the standard error?
127  */
128 void
129 cmdarg_err(const char *fmt, ...)
130 {
131   va_list ap;
132
133 #ifdef _WIN32
134   create_console();
135 #endif
136   va_start(ap, fmt);
137   fprintf(stderr, "dumpcap: ");
138   vfprintf(stderr, fmt, ap);
139   fprintf(stderr, "\n");
140   va_end(ap);
141 }
142
143 /*
144  * Report additional information for an error in command-line arguments.
145  * Creates a console on Windows.
146  * XXX - pop this up in a window of some sort on UNIX+X11 if the controlling
147  * terminal isn't the standard error?
148  */
149 void
150 cmdarg_err_cont(const char *fmt, ...)
151 {
152   va_list ap;
153
154 #ifdef _WIN32
155   create_console();
156 #endif
157   va_start(ap, fmt);
158   vfprintf(stderr, fmt, ap);
159   fprintf(stderr, "\n");
160   va_end(ap);
161 }
162
163
164 #ifdef _WIN32
165 BOOL WINAPI ConsoleCtrlHandlerRoutine(DWORD dwCtrlType)
166 {
167     /*printf("Event: %u", dwCtrlType);*/
168     capture_loop_stop();
169
170     return TRUE;
171 }
172 #endif
173
174 void
175 exit_main(int err) {
176 #ifdef _WIN32
177   /* Shutdown windows sockets */
178   WSACleanup();
179
180   destroy_console();
181 #endif
182
183   exit(err);
184 }
185
186
187 /* And now our feature presentation... [ fade to music ] */
188 int
189 main(int argc, char *argv[])
190 {
191   int                  opt;
192   extern char         *optarg;
193   gboolean             arg_error = FALSE;
194
195 #ifdef _WIN32
196   WSADATA              wsaData;
197 #endif  /* _WIN32 */
198
199   int                  err;
200   gboolean             start_capture = TRUE;
201   GList               *if_list;
202   if_info_t           *if_info;
203   GList               *lt_list, *lt_entry;
204   data_link_info_t    *data_link_info;
205   gchar                err_str[PCAP_ERRBUF_SIZE];
206   gchar               *cant_get_if_list_errstr;
207   gboolean             stats_known;
208   struct pcap_stat     stats;
209   GLogLevelFlags       log_flags;
210   gboolean             list_link_layer_types = FALSE;
211
212 #define OPTSTRING_INIT "a:b:c:f:Hhi:Lps:vW:w:y:"
213
214 #ifdef _WIN32
215 #define OPTSTRING_WIN32 "B:Z:"
216 #else
217 #define OPTSTRING_WIN32 ""
218 #endif  /* _WIN32 */
219
220   char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_WIN32) - 1] =
221     OPTSTRING_INIT OPTSTRING_WIN32;
222
223   /*** create the compile and runtime version strings ***/
224 #ifdef _WIN32
225   /* Load wpcap if possible. Do this before collecting the run-time version information */
226   load_wpcap();
227
228   /* ... and also load the packet.dll from wpcap */
229   wpcap_packet_load();
230
231   /* Start windows sockets */
232   WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
233
234
235   
236   SetConsoleCtrlHandler(&ConsoleCtrlHandlerRoutine, TRUE);
237 #endif  /* _WIN32 */
238
239   /* Assemble the compile-time version information string */
240   comp_info_str = g_string_new("Compiled ");
241   g_string_append(comp_info_str, "with ");
242   get_compiled_version_info(comp_info_str);
243
244   /* Assemble the run-time version information string */
245   runtime_info_str = g_string_new("Running ");
246   get_runtime_version_info(runtime_info_str);
247
248   ethereal_path = argv[0];
249
250   /* Arrange that if we have no console window, and a GLib message logging
251      routine is called to log a message, we pop up a console window.
252
253      We do that by inserting our own handler for all messages logged
254      to the default domain; that handler pops up a console if necessary,
255      and then calls the default handler. */
256
257   /* We might want to have component specific log levels later ... */
258
259   /* the default_log_handler will use stdout, which makes trouble with the */
260   /* capture child, as it uses stdout for it's sync_pipe */
261   /* so do the filtering in the console_log_handler and not here */
262   log_flags = 
263                     G_LOG_LEVEL_ERROR|
264                     G_LOG_LEVEL_CRITICAL|
265                     G_LOG_LEVEL_WARNING|
266                     G_LOG_LEVEL_MESSAGE|
267                     G_LOG_LEVEL_INFO|
268                     G_LOG_LEVEL_DEBUG|
269                     G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION;
270
271   g_log_set_handler(NULL,
272                     log_flags,
273                     console_log_handler, NULL /* user_data */);
274   g_log_set_handler(LOG_DOMAIN_MAIN,
275                     log_flags,
276                     console_log_handler, NULL /* user_data */);
277   g_log_set_handler(LOG_DOMAIN_CAPTURE,
278                     log_flags,
279             console_log_handler, NULL /* user_data */);
280   g_log_set_handler(LOG_DOMAIN_CAPTURE_CHILD,
281                     log_flags,
282             console_log_handler, NULL /* user_data */);
283
284   /* Set the initial values in the capture_opts. This might be overwritten 
285      by preference settings and then again by the command line parameters. */
286   capture_opts_init(capture_opts, NULL);
287
288   capture_opts->snaplen             = MIN_PACKET_SIZE;
289   capture_opts->has_ring_num_files  = TRUE;
290
291   /* Now get our args */
292   while ((opt = getopt(argc, argv, optstring)) != -1) {
293     switch (opt) {
294       case 'h':        /* Print help and exit */
295         print_usage(TRUE);
296         exit_main(0);
297         break;
298       case 'v':        /* Show version and exit */
299         show_version();
300         exit_main(0);
301         break;
302       /*** capture option specific ***/
303       case 'a':        /* autostop criteria */
304       case 'b':        /* Ringbuffer option */
305       case 'c':        /* Capture xxx packets */
306       case 'f':        /* capture filter */
307       case 'H':        /* Hide capture info dialog box */
308       case 'i':        /* Use interface xxx */
309       case 'p':        /* Don't capture in promiscuous mode */
310       case 's':        /* Set the snapshot (capture) length */
311       case 'w':        /* Write to capture file xxx */
312       case 'y':        /* Set the pcap data link type */
313 #ifdef _WIN32
314       case 'B':        /* Buffer size */
315       /* Hidden option supporting Sync mode */
316       case 'Z':        /* Write to pipe FD XXX */
317 #endif /* _WIN32 */
318         capture_opts_add_opt(capture_opts, opt, optarg, &start_capture);
319         break;
320       /* This is a hidden option supporting Sync mode, so we don't set
321        * the error flags for the user in the non-libpcap case.
322        */
323       case 'W':        /* Write to capture file FD xxx */
324         capture_opts_add_opt(capture_opts, opt, optarg, &start_capture);
325         break;
326
327       /*** all non capture option specific ***/
328       case 'L':        /* Print list of link-layer types and exit */
329         list_link_layer_types = TRUE;
330         break;
331       default:
332       case '?':        /* Bad flag - print usage message */
333         arg_error = TRUE;
334         break;
335     }
336   }
337   argc -= optind;
338   argv += optind;
339   if (argc >= 1) {
340       /* user specified file name as regular command-line argument */
341       /* XXX - use it as the capture file name (or somthing else)? */
342     argc--;
343     argv++;
344   }
345
346   if (argc != 0) {
347     /*
348      * Extra command line arguments were specified; complain.
349      */
350     cmdarg_err("Invalid argument: %s", argv[0]);
351     arg_error = TRUE;
352   }
353
354   if (arg_error) {
355     print_usage(FALSE);
356     exit_main(1);
357   }
358
359   if (list_link_layer_types) {
360     /* We're supposed to list the link-layer types for an interface;
361        did the user also specify a capture file to be read? */
362     /* No - did they specify a ring buffer option? */
363     if (capture_opts->multi_files_on) {
364       cmdarg_err("Ring buffer requested, but a capture isn't being done.");
365       exit_main(1);
366     }
367   } else {
368     /* No - was the ring buffer option specified and, if so, does it make
369        sense? */
370     if (capture_opts->multi_files_on) {
371       /* Ring buffer works only under certain conditions:
372          a) ring buffer does not work with temporary files;
373          b) it makes no sense to enable the ring buffer if the maximum
374             file size is set to "infinite". */
375       if (capture_opts->save_file == NULL) {
376         cmdarg_err("Ring buffer requested, but capture isn't being saved to a permanent file.");
377         capture_opts->multi_files_on = FALSE;
378       }
379       if (!capture_opts->has_autostop_filesize && !capture_opts->has_file_duration) {
380         cmdarg_err("Ring buffer requested, but no maximum capture file size or duration were specified.");
381 /* XXX - this must be redesigned as the conditions changed */
382 /*      capture_opts->multi_files_on = FALSE;*/
383       }
384     }
385   }
386
387 /* Did the user specify an interface to use? */
388 if (capture_opts->iface == NULL) {
389     /* No - pick the first one from the list of interfaces. */
390     if_list = get_interface_list(&err, err_str);
391     if (if_list == NULL) {
392       switch (err) {
393
394       case CANT_GET_INTERFACE_LIST:
395           cant_get_if_list_errstr = cant_get_if_list_error_message(err_str);
396           cmdarg_err("%s", cant_get_if_list_errstr);
397           g_free(cant_get_if_list_errstr);
398           break;
399
400       case NO_INTERFACES_FOUND:
401           cmdarg_err("There are no interfaces on which a capture can be done");
402           break;
403       }
404       exit_main(2);
405     }
406     if_info = if_list->data;    /* first interface */
407     capture_opts->iface = g_strdup(if_info->name);
408     free_interface_list(if_list);
409   }
410
411   if (list_link_layer_types) {
412     /* Get the list of link-layer types for the capture device. */
413     lt_list = get_pcap_linktype_list(capture_opts->iface, err_str);
414     if (lt_list == NULL) {
415       if (err_str[0] != '\0') {
416         cmdarg_err("The list of data link types for the capture device could not be obtained (%s)."
417           "Please check to make sure you have sufficient permissions, and that\n"
418           "you have the proper interface or pipe specified.\n", err_str);
419       } else
420         cmdarg_err("The capture device has no data link types.");
421       exit_main(2);
422     }
423     g_warning("Data link types (use option -y to set):");
424     for (lt_entry = lt_list; lt_entry != NULL;
425          lt_entry = g_list_next(lt_entry)) {
426       data_link_info = lt_entry->data;
427       g_warning("  %s", data_link_info->name);
428       if (data_link_info->description != NULL)
429         g_warning(" (%s)", data_link_info->description);
430       else
431         g_warning(" (not supported)");
432       putchar('\n');
433     }
434     free_pcap_linktype_list(lt_list);
435     exit_main(0);
436   }
437
438   if (capture_opts->has_snaplen) {
439     if (capture_opts->snaplen < 1)
440       capture_opts->snaplen = WTAP_MAX_PACKET_SIZE;
441     else if (capture_opts->snaplen < MIN_PACKET_SIZE)
442       capture_opts->snaplen = MIN_PACKET_SIZE;
443   }
444
445   /* Check the value range of the ringbuffer_num_files parameter */
446   if (capture_opts->ring_num_files > RINGBUFFER_MAX_NUM_FILES)
447     capture_opts->ring_num_files = RINGBUFFER_MAX_NUM_FILES;
448 #if RINGBUFFER_MIN_NUM_FILES > 0
449   else if (capture_opts->num_files < RINGBUFFER_MIN_NUM_FILES)
450     capture_opts->ring_num_files = RINGBUFFER_MIN_NUM_FILES;
451 #endif
452
453   /* Now start the capture. */
454
455   /* XXX - hand the stats to the parent process */
456   if(capture_loop_start(capture_opts, &stats_known, &stats) == TRUE) {
457       /* capture ok */
458       err = 0;
459   } else {
460       /* capture failed */
461       err = 1;
462   }
463
464   /* the capture is done; there's nothing more for us to do. */
465   exit_main(err);
466 }
467
468 #ifdef _WIN32
469
470 /* We build this as a GUI subsystem application on Win32, so
471    "WinMain()", not "main()", gets called.
472
473    Hack shamelessly stolen from the Win32 port of the GIMP. */
474 #ifdef __GNUC__
475 #define _stdcall  __attribute__((stdcall))
476 #endif
477
478 int _stdcall
479 WinMain (struct HINSTANCE__ *hInstance,
480          struct HINSTANCE__ *hPrevInstance,
481          char               *lpszCmdLine,
482          int                 nCmdShow)
483 {
484   has_console = FALSE;
485   return main (__argc, __argv);
486 }
487
488 /*
489  * If this application has no console window to which its standard output
490  * would go, create one.
491  */
492 void
493 create_console(void)
494 {
495   if (!has_console) {
496     /* We have no console to which to print the version string, so
497        create one and make it the standard input, output, and error. */
498     if (!AllocConsole())
499       return;   /* couldn't create console */
500     eth_freopen("CONIN$", "r", stdin);
501     eth_freopen("CONOUT$", "w", stdout);
502     eth_freopen("CONOUT$", "w", stderr);
503
504     /* Well, we have a console now. */
505     has_console = TRUE;
506
507     /* Now register "destroy_console()" as a routine to be called just
508        before the application exits, so that we can destroy the console
509        after the user has typed a key (so that the console doesn't just
510        disappear out from under them, giving the user no chance to see
511        the message(s) we put in there). */
512     atexit(destroy_console);
513
514     SetConsoleTitle("Dumpcap Console");
515   }
516 }
517
518 static void
519 destroy_console(void)
520 {
521   if (has_console) {
522     printf("\n\nPress any key to exit\n");
523     _getch();
524     FreeConsole();
525   }
526 }
527 #endif /* _WIN32 */
528
529
530 /* This routine should not be necessary, at least as I read the GLib
531    source code, as it looks as if GLib is, on Win32, *supposed* to
532    create a console window into which to display its output.
533
534    That doesn't happen, however.  I suspect there's something completely
535    broken about that code in GLib-for-Win32, and that it may be related
536    to the breakage that forces us to just call "printf()" on the message
537    rather than passing the message on to "g_log_default_handler()"
538    (which is the routine that does the aforementioned non-functional
539    console window creation). */
540 static void
541 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
542                     const char *message, gpointer user_data _U_)
543 {
544   time_t curr;
545   struct tm *today;
546   const char *level;
547
548
549   /* ignore log message, if log_level isn't interesting */
550   if( !(log_level & G_LOG_LEVEL_MASK & ~G_LOG_LEVEL_DEBUG /*prefs.console_log_level*/)) {
551     return;
552   }
553
554   /* create a "timestamp" */
555   time(&curr);
556   today = localtime(&curr);    
557
558 #ifdef _WIN32
559 /*  if (prefs.gui_console_open != console_open_never) {*/
560     create_console();
561 /*  }*/
562   if (has_console) {
563     /* For some unknown reason, the above doesn't appear to actually cause
564        anything to be sent to the standard output, so we'll just splat the
565        message out directly, just to make sure it gets out. */
566 #endif
567     switch(log_level & G_LOG_LEVEL_MASK) {
568     case G_LOG_LEVEL_ERROR:
569         level = "Err ";
570         break;
571     case G_LOG_LEVEL_CRITICAL:
572         level = "Crit";
573         break;
574     case G_LOG_LEVEL_WARNING:
575         level = "Warn";
576         break;
577     case G_LOG_LEVEL_MESSAGE:
578         level = "Msg ";
579         break;
580     case G_LOG_LEVEL_INFO:
581         level = "Info";
582         break;
583     case G_LOG_LEVEL_DEBUG:
584         level = "Dbg ";
585         break;
586     default:
587         fprintf(stderr, "unknown log_level %u\n", log_level);
588         level = NULL;
589         g_assert_not_reached();
590     }
591
592     /* don't use printf (stdout), as the capture child uses stdout for it's sync_pipe */
593     fprintf(stderr, "%02u:%02u:%02u %8s %s %s\n",
594             today->tm_hour, today->tm_min, today->tm_sec,
595             log_domain != NULL ? log_domain : "",
596             level, message);
597 #ifdef _WIN32
598   } else {
599     g_log_default_handler(log_domain, log_level, message, user_data);
600   }
601 #endif
602 }
603
604
605 /* Size of buffer to hold decimal representation of
606    signed/unsigned 64-bit int */
607 #define SP_DECISIZE 20
608
609 /*
610  * Indications sent out on the sync pipe.
611  */
612 #define SP_FILE         'F'     /* the name of the recently opened file */
613 #define SP_ERROR_MSG    'E'     /* error message */
614 #define SP_PACKET_COUNT 'P'     /* count of packets captured since last message */
615 #define SP_DROPS        'D'     /* count of packets dropped in capture */
616 #define SP_QUIT         'Q'     /* capture quit message (from parent to child) */
617
618 static void
619 pipe_write_block(int pipe, char indicator, int len, const char *msg)
620 {
621 }
622
623 void
624 sync_pipe_packet_count_to_parent(int packet_count)
625 {
626     char tmp[SP_DECISIZE+1+1];
627
628     g_snprintf(tmp, sizeof(tmp), "%d", packet_count);
629
630     /*g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_packet_count_to_parent: %s", tmp);*/
631
632     pipe_write_block(1, SP_PACKET_COUNT, strlen(tmp)+1, tmp);
633 }
634
635 void
636 sync_pipe_filename_to_parent(const char *filename)
637 {
638     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "File: %s", filename);
639
640     pipe_write_block(1, SP_FILE, strlen(filename)+1, filename);
641 }
642
643 void
644 sync_pipe_errmsg_to_parent(const char *errmsg)
645 {
646     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_errmsg_to_parent: %s", errmsg);
647
648     pipe_write_block(1, SP_ERROR_MSG, strlen(errmsg)+1, errmsg);
649 }
650
651 void
652 sync_pipe_drops_to_parent(int drops)
653 {
654     char tmp[SP_DECISIZE+1+1];
655
656
657     g_snprintf(tmp, sizeof(tmp), "%d", drops);
658
659     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_drops_to_parent: %s", tmp);
660
661     pipe_write_block(1, SP_DROPS, strlen(tmp)+1, tmp);
662 }
663
664
665
666 /****************************************************************************************************************/
667 /* link "dummies" */
668
669
670 void main_window_update(void) {}
671
672
673
674 void capture_info_ui_create(capture_info *cinfo, gchar *iface) {}
675
676 void capture_info_ui_update(capture_info *cinfo) {
677     printf("Packets: %u\r", cinfo->counts->total);
678 }
679
680 void capture_info_ui_destroy(capture_info *cinfo) {}
681
682
683 static gpointer *
684 display_simple_dialog(gint type, gint btn_mask, char *message)
685 {
686     printf("%s", message);
687
688     return NULL;
689 }
690
691 char *simple_dialog_primary_start(void)
692 {
693     return "";
694 }
695
696 char *simple_dialog_primary_end(void)
697 {
698     return "";
699 }
700
701 /* Simple dialog function - Displays a dialog box with the supplied message
702  * text.
703  *
704  * Args:
705  * type       : One of ESD_TYPE_*.
706  * btn_mask   : The value passed in determines which buttons are displayed.
707  * msg_format : Sprintf-style format of the text displayed in the dialog.
708  * ...        : Argument list for msg_format
709  */
710
711 gpointer
712 vsimple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, va_list ap)
713 {
714   gchar             *vmessage;
715   gchar             *message;
716   gpointer          *win;
717 #if GTK_MAJOR_VERSION >= 2
718   GdkWindowState state = 0;
719 #endif
720
721   /* Format the message. */
722   vmessage = g_strdup_vprintf(msg_format, ap);
723
724 #if GTK_MAJOR_VERSION >= 2
725   /* convert character encoding from locale to UTF8 (using iconv) */
726   message = g_locale_to_utf8(vmessage, -1, NULL, NULL, NULL);
727   g_free(vmessage);
728 #else
729   message = vmessage;
730 #endif
731
732   win = display_simple_dialog(type, btn_mask, message);
733
734   g_free(message);
735
736   return win;
737 }
738
739 gpointer
740 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
741 {
742   va_list ap;
743   gpointer ret;
744
745   va_start(ap, msg_format);
746   ret = vsimple_dialog(type, btn_mask, msg_format, ap);
747   va_end(ap);
748   return ret;
749 }
750
751 char *
752 simple_dialog_format_message(const char *msg)
753 {
754     char *str;
755
756     if (msg) {
757 #if GTK_MAJOR_VERSION < 2
758         str = g_strdup(msg);
759 #else
760         str = xml_escape(msg);
761 #endif
762     } else {
763         str = NULL;
764     }
765     return str;
766 }
767
768
769 /*
770  * Find out whether a hostname resolves to an ip or ipv6 address
771  * Return "ip6" if it is IPv6, "ip" otherwise (including the case
772  * that we don't know)
773  */
774 const char* host_ip_af(const char *host
775 #ifndef HAVE_GETHOSTBYNAME2
776 _U_
777 #endif
778 )
779 {
780 #ifdef HAVE_GETHOSTBYNAME2
781         struct hostent *h;
782         return (h = gethostbyname2(host, AF_INET6)) && h->h_addrtype == AF_INET6 ? "ip6" : "ip";
783 #else
784         return "ip";
785 #endif
786 }
787