/*
* $id: uii_commands.c,v 1.1.1.1 1998/08/10 20:06:56 dogcow Exp $
* originally Id: uii_commands.c,v 1.19 1998/06/15 16:20:36 gerald Exp
*/
#include <stdio.h>
#include <string.h>
#include "mrt.h"
#include "trace.h"
#include <time.h>
#include <signal.h>
#include "config_file.h"
#include <sys/types.h>
#include <ctype.h>
#include "irrd.h"
#include <errno.h>
#include <fcntl.h>
#ifndef SETPGRP_VOID
#endif
#ifndef localtime_r /* Not present on SunOS, at least. */
#define localtime_r(a,b) localtime(a)
#endif
extern trace_t *default_trace;
/* Handler routines for IRR lookups
* the show_xxx functions are human readable UII commands
* the irr_xxx are used by the RAWhoisd machine telnet interface
*/
void uii_show_ip_less (uii_connection_t *uii, prefix_t *prefix, int flag);
void uii_show_ip_exact (uii_connection_t *uii, prefix_t *prefix);
void uii_show_ip_more (uii_connection_t *uii, prefix_t *prefix);
static void run_cmd (char *cmd, FILE **in, FILE **out);
void show_database (uii_connection_t *uii) {
irr_database_t *database;
char buf[BUFSIZE], tmp[BUFSIZE], *p_last_export;
int total_size, total_rt, total_aut;
uii_add_bulk_output (uii, "Listening on port %d (fd=%d)", IRR.irr_port, IRR.sockfd);
if (IRR.whois_port > 0) {
uii_add_bulk_output (uii, ", and port %d (fd=%d)", IRR.whois_port, IRR.sockfd);
}
uii_add_bulk_output (uii, "\r\n");
uii_add_bulk_output (uii, "Memory-only indexing\r\n");
if (IRR.database_syntax == RIPE181) {
uii_add_bulk_output (uii, "RIPE181 Syntax\r\n\r\n");
}
else if (IRR.database_syntax == RPSL) {
uii_add_bulk_output (uii, "RPSL Syntax\r\n\r\n");
}
else if (IRR.database_syntax == EMPTY_DB) {
uii_add_bulk_output (uii, "Unknown Syntax, all DB's empty\r\n\r\n");
}
else if (IRR.database_syntax == MIXED) {
uii_add_bulk_output (uii, "RIPE181 and RPSL Syntaxes\r\n\r\n");
}
else {
uii_add_bulk_output (uii, "** Unknown Database Syntax\r\n\r\n");
}
uii_add_bulk_output (uii, "Default Database Query Order: ");
LL_Iterate (IRR.ll_database, database) {
uii_add_bulk_output (uii, "%s ", database->name);
}
uii_add_bulk_output (uii, "\r\n\r\n");
uii_add_bulk_output (uii, "%10s %-9s %6s %10s %8s %s",
"Database", "Size (kb)", "Rt Obj",
"AutNum Obj", "Serial #", "Last Export #\r\n");
uii_add_bulk_output (uii, "%6s %-9s %6s %10s %4s %s",
"-------------", "---------", "------",
"----------", "--------", "-------------\r\n");
total_size = total_rt = total_aut = 0;
LL_Iterate (IRR.ll_database_alphabetized, database) {
sprintf (buf, "%8.1f", ((float) database->bytes / 1000.0));
uii_add_bulk_output (uii, " %-12s %9s",
database->name, buf);
strcpy (tmp, database->name);
convert_toupper (tmp);
p_last_export = GetStatusString (IRR.statusfile, tmp, "lastexport");
uii_add_bulk_output (uii, " %6d %10d %8d %13s\r\n",
database->num_objects[ROUTE],
database->num_objects[AUT_NUM],
database->serial_number,
(p_last_export == NULL) ? "" : p_last_export);
total_size += database->bytes;
total_rt += database->num_objects[ROUTE];
total_aut += database->num_objects[AUT_NUM];
}
sprintf (buf, "%8.1f", ((float) total_size / 1000.0));
uii_add_bulk_output (uii, " %-12s %9s %6d %10d\r\n",
"TOTAL", buf, total_rt, total_aut);
uii_add_bulk_output (uii, "\r\n");
LL_Iterate (IRR.ll_database_alphabetized, database) {
uii_add_bulk_output (uii, "%s ", database->name);
if (database->flags & IRR_AUTHORITATIVE)
uii_add_bulk_output (uii, " AUTHORITATIVE");
if (database->obj_filter > 0)
uii_add_bulk_output (uii, " (filter 0x%x)", database->obj_filter);
uii_add_bulk_output (uii, "\r\n");
/* mirroring */
if (database->mirror_prefix != NULL) {
nice_time (time_left (database->mirror_timer), buf);
uii_add_bulk_output (uii, " Mirroring %s:%d (Next in %s)\r\n",
prefix_toa (database->mirror_prefix),
database->mirror_port,
buf);
if (database->mirror_error_message[0] != '\0') {
if (database->mirror_error_message[strlen(database->mirror_error_message) -1] == '\n')
database->mirror_error_message[strlen(database->mirror_error_message) -1] = '\0';
uii_add_bulk_output (uii, " %s \r\n", database->mirror_error_message);
}
if (database->mirror_fd != -1) {
uii_add_bulk_output (uii, " MIRRORING (%d bytes) (%d seconds)\r\n",
database->mirror_update_size,
time (NULL) - database->mirror_started);
}
else if (database->last_mirrored <= 0) {
uii_add_bulk_output (uii, " Never mirrored\r\n");
}
else {
char tmpt[BUFSIZE];
strftime (tmpt, BUFSIZE, "%T %m/%d/%Y",
localtime_r ((time_t *) &database->last_mirrored, &my_tm));
uii_add_bulk_output (uii, " Last mirrored %s\r\n", tmpt);
uii_add_bulk_output (uii, " %d bytes, %d change(s)\r\n",
database->mirror_update_size, database->num_changes);
}
}
/*
* mirroring not turned on
* Either loaded, or email
*/
else {
char tmpt[BUFSIZE];
if (database->flags & IRR_AUTHORITATIVE) {
if (database->last_update == 0)
uii_add_bulk_output (uii, " Last email/tcp update Never\r\n");
else {
strftime (tmpt, BUFSIZE, "%T %m/%d/%Y",
localtime_r ((time_t *) &database->last_update, &my_tm));
uii_add_bulk_output (uii, " Last email/tcp update %s\r\n", tmpt);
}
}
if (database->time_loaded == 0)
uii_add_bulk_output (uii, " Last loaded Never\r\n");
else {
strftime (tmpt, BUFSIZE, "%T %m/%d/%Y",
localtime_r ((time_t *) &database->time_loaded, &my_tm));
uii_add_bulk_output (uii, " Last loaded %s\r\n", tmpt);
}
}
if (database->export_timer != NULL) {
nice_time (time_left (database->export_timer), buf);
uii_add_bulk_output (uii, " Next export in %s\r\n", buf);
}
if (database->no_dbclean) {
uii_add_bulk_output (uii, " Cleaning disabled\r\n");
}
else if (database->clean_timer != NULL) {
nice_time (time_left (database->clean_timer), buf);
uii_add_bulk_output (uii, " Next dbclean in %s\r\n", buf);
}
}
uii_send_bulk_data (uii);
}
/* uii_irr_reload
* Reload the database
*/
void uii_irr_reload (uii_connection_t *uii, char *name) {
if (name != NULL) {
irr_reload_database (name, uii, NULL);
Delete (name);
}
}
#ifndef NT
/* Invoke irrdcacher to fetch a DB and call 'irr_reload_database ()'
* to reload it.
*
* sample uii command: 'irrdcacher bell'
*
* Function can be called from the uii or from 'irr_load_data ()'
* which is invoked in main.c on bootstrap. 'irr_load_data ()'
* loads the DB atomically
*
* Function relies on irrdcacher and wget being installed and in
* the search path. The search path can be augmented with the
* 'irr_path' config command. There are no assumptions about
* the availability of irrdcacher and if found this routine will
* parse the irrdcacher output to determine the result.
*
* Input:
* -data struct for communicating with UII users (uii)
* -DB name to fetch (name)
*
* Return:
* -1 if the DB wsa fetched and reloaded without error
* -0 otherwise
*/
int uii_irr_irrdcacher (uii_connection_t *uii, char *name) {
int ret_code = 0;
FILE *pipeout;
char cmd[BUFSIZE + 1], cs[BUFSIZE + 1], wflag[BUFSIZE + 1], *tmp_dir;
irr_database_t *db;
regex_t url_re = {0}, good_re = {0}, notfound_re = {0};
char *ftp_url = "^ftp://[^ \t/]+/[[:graph:]]+$";
char *good = "^Successful operation";
char *notfound = "not found[ \t]*\n$";
/* sanity checks */
/* do we know about this DB? */
if (name == NULL ||
(db = find_database (name)) == NULL) {
if (uii != NULL)
uii_send_data (uii, "Unknown DB (%s)\r\n", ((name == NULL) ? "NULL" : name));
goto ABORT_IRRDCACHER;
}
/* it's a mistake to fetch an authoritative DB */
else if (db->flags & IRR_AUTHORITATIVE) {
if (uii != NULL)
uii_send_data (uii, "ERROR: (%s) is declared \"authoritative\" \r\n", name);
goto ABORT_IRRDCACHER;
}
/* see if a remote ftp dir has been configured */
else if (db->remote_ftp_url == NULL) {
if (uii != NULL) {
uii_send_data (uii,
"No ftp remote directory has been configured for (%s)\r\n", name);
uii_send_data (uii, "Type \"config\" and then \"irr_database %s remote_ftp_url <ftp URL>\"\r\n", name);
}
goto ABORT_IRRDCACHER;
}
/* compile our reg ex's */
regcomp (&url_re, ftp_url, REG_EXTENDED|REG_NOSUB);
regcomp (&good_re, good, REG_EXTENDED|REG_NOSUB);
regcomp (¬found_re, notfound, REG_EXTENDED|REG_NOSUB);
/* do we have a well formed ftp URL? */
if (regexec (&url_re, db->remote_ftp_url, 0, NULL, 0)) {
if (uii != NULL) {
uii_send_data (uii, "Invalid ftp URL for (%s)!\r\n", db->name);
uii_send_data (uii, "Expect (ftp://<remote server>/<remote directory>)\r\n", name);
}
goto ABORT_IRRDCACHER;
}
/* build the irrdcacher command invokation */
/* IRR.path is used to add a component to the current path, ie,
* might be needed to find irrdcacher and wget */
cmd[0] = wflag[0] = '\0';
if (IRR.path != NULL) {
sprintf (cmd, "PATH=${PATH}%s%s;export PATH;", ((*IRR.path == ':') ? "" : ":"),
IRR.path);
sprintf (wflag, "-w %s", IRR.path);
}
#ifdef HAVE_WGET
{ char *p;
if (wflag[0] == '\0')
sprintf (wflag, "-w ");
sprintf (&wflag[strlen (wflag)], ":%s", WGET_CMD);
if ((p = strrchr (wflag, '/')) != NULL)
*p = '\0';
}
#endif
trace (NORM, default_trace, "JW: wflag (%s)\n", wflag);
/* dir for irrdcacher to use to place to bring the files onto our host */
if ((tmp_dir = IRR.tmp_dir) == NULL)
tmp_dir = "/var/tmp";
/* build the irrdcacher invokation */
#ifdef HAVE_IRRDCACHER
sprintf (&cmd[strlen (cmd)], "%s", IRRDCACHER_CMD);
#else
sprintf (&cmd[strlen (cmd)], "%s", "irrdcacher");
#endif
sprintf (&cmd[strlen (cmd)],
" -S %s -s %s %s.db.gz ", wflag,
db->remote_ftp_url, name);
/* we need the *.CURRENTSERIAL file also for mirrored files */
if (db->mirror_prefix != NULL) {
/* need to make the DB name upper case */
strcpy (cs, name);
convert_toupper (cs);
sprintf (&cmd[strlen (cmd)], " %s.CURRENTSERIAL", cs);
}
trace (NORM, default_trace, "JW: cmd (%s)\n", cmd);
if (uii != NULL)
uii_send_data (uii, "Fetching DB remotely. Please be patient...\r\n");
/* invoke irrdcacher */
run_cmd (cmd, NULL, &pipeout);
if (pipeout == NULL) {
if (uii != NULL)
uii_send_data (uii, "irrdcacher not found or no \"sh\" found.\r\n");
goto ABORT_IRRDCACHER;
}
/* parse the irrdcacher output, look at the last line only */
cmd[0] = '\0';
while (fgets (cmd, BUFSIZE, pipeout) != NULL) {
trace (NORM, default_trace, "irrdcacher: %s", cmd);
if (uii != NULL)
uii_send_data (uii, "%s", cmd);
}
fclose (pipeout);
/* move the DB into our cache and reload */
if (regexec (&good_re, cmd, 0, NULL, 0) == 0)
ret_code = irr_reload_database (name, uii, tmp_dir);
/* "sh" not installed */
else if (cmd[0] == '\0') {
if (uii != NULL)
uii_send_data (uii, "/bin/sh not installed. Could not run irrdacher.\r\n");
}
/* irrdcacher not found */
else if (regexec (¬found_re, cmd, 0, NULL, 0) == 0) {
if (uii != NULL) {
uii_send_data (uii, "Couldn't find irrdcacher on your system\r\n");
uii_send_data (uii, "irrd requires both 'irrdcacher' and 'wget' for remote ftp access\r\n");
uii_send_data (uii, "Reset your path with \"irr_path\". eg, irr_path /usr/mybin\r\n");
}
}
else if (uii != NULL) {
uii_send_data (uii, "%s\n", cmd);
uii_send_data (uii, "Operation unsuccessful!\n");
}
ABORT_IRRDCACHER:
/* clean up */
Delete (name);
regfree (&good_re);
regfree (&url_re);
regfree (¬found_re);
return ret_code;
}
#endif /* NT */
void uii_export_database (uii_connection_t *uii, char *name) {
irr_database_t *db;
db = find_database (name);
if (IRR.ftp_dir == NULL) {
uii_send_data (uii, "Error -- no ftp directory configured.\r\n");
Delete (name);
return;
}
if (db != NULL) {
uii_send_data (uii, "Exporting %s database to %s...\r\n",
name, IRR.ftp_dir);
if (irr_database_export (db) != 1) {
uii_send_data (uii, "ERROR exporting %s database -- see log for more information\r\n",
name);
}
else {
uii_send_data (uii, "Export successful for %s\r\n", name);
if (db->export_timer != NULL)
Timer_Reset_Time (db->export_timer);
}
}
else
uii_send_data (uii, "Could not find %s database...\r\n", name);
Delete (name);
return;
}
void uii_irr_clean (uii_connection_t *uii, char *name) {
irr_database_t *db;
db = find_database (name);
if (db != NULL) {
uii_send_data (uii, "Cleaning %s database...\r\n", name);
irr_database_clean (db);
if (db->clean_timer != NULL)
Timer_Reset_Time (db->clean_timer);
}
else
uii_send_data (uii, "Could not find %s database...\r\n", name);
Delete (name);
return;
}
void uii_irr_mirror_last (uii_connection_t *uii, char *name) {
uii_irr_mirror (uii, name, 0);
}
void uii_irr_mirror_serial (uii_connection_t *uii, char *name, int serial) {
uii_irr_mirror (uii, name, serial);
}
void uii_irr_mirror (uii_connection_t *uii, char *name, int serial) {
irr_database_t *database;
database = find_database (name);
if (database == NULL) {
config_notice (ERROR, uii, "Database not found\r\n");
Delete (name);
return;
}
if (database->mirror_prefix == NULL) {
uii_send_data (uii, "Don't know how to mirror %s\r\n", database->name);
return;
}
uii_send_data (uii, "Mirroring %s database...\r\n", name);
uii_send_data (uii, "Current serial number %d\r\n", database->serial_number);
if (serial > 0)
uii_send_data (uii, "serial to: %d\n", serial);
else
uii_send_data (uii, "serial to: LAST\n");
uii_send_data (uii, "Starting mirror in the background......\r\n",
database->serial_number);
if (request_mirror (database, uii, serial) == -1) {
uii_send_data (uii, "\r\n** ERROR ** mirroring database!\r\n");
uii_send_data (uii, "See logfile for more information\r\n");
Delete (name);
return;
}
if (database->mirror_timer != NULL)
Timer_Reset_Time (database->mirror_timer);
Delete (name);
return;
}
/* mainly for debugging, but would be nice to be able to set the serial number */
void uii_set_serial (uii_connection_t *uii, char *name, int serial) {
irr_database_t *database;
if ((database = find_database (name)) != NULL) {
database->serial_number = serial;
Delete (name);
return;
}
uii_send_data (uii, "%s database not found\r\n", name);
Delete (name);
}
void uii_show_ip (uii_connection_t *uii, prefix_t *prefix, int num, char *lessmore) {
if (num == 0) {
uii_show_ip_exact (uii, prefix);
return;
}
if (!strcmp (lessmore, "less")) {
uii_show_ip_less (uii, prefix, 1);
return;
}
else {
uii_show_ip_more (uii, prefix);
return;
}
/*NOTREACHED*/
return;
}
void uii_fetch_irr_object (uii_connection_t *uii,
irr_database_t *database, u_long offset) {
char buffer[BUFSIZE+1];
fseek (database->db_fp, offset, SEEK_SET);
while (fgets (buffer, BUFSIZE, database->db_fp) != NULL) {
buffer[strlen(buffer) -1] = '\0'; /* lose newline */
if (strlen (buffer) < 2) break;
uii_add_bulk_output (uii, "%s\r\n", buffer);
}
}
void uii_show_ip_exact (uii_connection_t *uii, prefix_t *prefix) {
radix_node_t *node;
LINKED_LIST *ll_attr;
irr_route_object_t *attr;
irr_database_t *database;
int first = 1;
LL_Iterate (IRR.ll_database, database) {
irr_lock (database);
node = radix_search_exact (database->radix, prefix);
if (node != NULL) {
ll_attr = (LINKED_LIST *) node->data;
LL_Iterate (ll_attr, attr) {
if (first != 1) uii_add_bulk_output (uii, "\r\n");
first = 0;
uii_fetch_irr_object (uii, database, attr->offset);
}
}
irr_unlock (database);
}
uii_send_bulk_data (uii);
}
void uii_show_ip_less (uii_connection_t *uii, prefix_t *prefix, int flag) {
radix_node_t *node;
LINKED_LIST *ll_attr;
irr_route_object_t *attr;
irr_database_t *database;
prefix_t *tmp_prefix;
int first = 1;
LL_Iterate (IRR.ll_database, database) {
irr_lock (database);
tmp_prefix = prefix;
/* first check if this node exists */
if ((node = radix_search_exact (database->radix, prefix)) != NULL) {
ll_attr = (LINKED_LIST *) node->data;
LL_Iterate (ll_attr, attr) {
if (first != 1) uii_add_bulk_output (uii, "\r\n");
first = 0;
uii_fetch_irr_object (uii, database, attr->offset);
}
}
/* now check all less specific */
while ((flag == 1 || node == NULL) &&
(node = radix_search_best (database->radix, tmp_prefix, 0)) != NULL) {
if (node != NULL) {
tmp_prefix = node->prefix;
ll_attr = (LINKED_LIST *) node->data;
LL_Iterate (ll_attr, attr) {
if (first != 1) uii_add_bulk_output (uii, "\r\n");
first = 0;
uii_fetch_irr_object (uii, database, attr->offset);
}
}
/* break out after one loop for "l" case */
if (flag == 0) break;
}
irr_unlock (database);
}
uii_send_bulk_data (uii);
}
/* Route searches. M - all more specific eg, !r199.208.0.0/16,M */
/* Also does m - one level only more specific */
void uii_show_ip_more (uii_connection_t *uii, prefix_t *prefix) {
radix_node_t *node, *start_node, *last_node;
LINKED_LIST *ll_attr;
irr_route_object_t *attr;
irr_database_t *database;
int first = 1;
int len;
LL_Iterate (IRR.ll_database, database) {
irr_lock (database);
last_node = NULL;
start_node = NULL;
node = NULL;
/* first, try to find this prefix exactly */
start_node = radix_search_exact (database->radix, prefix);
/* fine, try to find something less specific */
if (start_node == NULL) {
start_node = radix_search_best (database->radix, prefix, 0);
}
/* yuck -- walk up the radix tree until find more specific that
* does not match
*/
if (start_node == NULL) {
prefix_t *tmp_prefix;
len = prefix->bitlen;
prefix->bitlen = 32;
tmp_prefix = prefix;
while ((start_node = radix_search_best (database->radix, tmp_prefix, 0)) != NULL) {
if (!comp_with_mask ((void *) prefix_tochar (start_node->prefix),
(void *) prefix_tochar (prefix), len))
break;
tmp_prefix = start_node->prefix;
last_node = start_node;
}
prefix->bitlen = len;
if (start_node == NULL) start_node = last_node;
}
if (start_node != NULL) {
RADIX_WALK (start_node, node) {
if ((node->prefix->bitlen >= prefix->bitlen) &&
(comp_with_mask ((void *) prefix_tochar (node->prefix),
(void *) prefix_tochar (prefix), prefix->bitlen))) {
ll_attr = (LINKED_LIST *) node->data;
LL_Iterate (ll_attr, attr) {
if (first != 1) {
uii_add_bulk_output (uii, "\r\n");
}
first = 0;
uii_fetch_irr_object (uii, database, attr->offset);
}
}
}
RADIX_WALK_END;
}
irr_unlock (database);
}
uii_send_bulk_data (uii);
}
/* uii_delete_route
* mainly for testing/debugging
*/
int uii_delete_route (uii_connection_t *uii, char *name, prefix_t *prefix, int as) {
irr_database_t *db;
irr_object_t *irr_object;
db = find_database (name);
if (db == NULL) {
uii_send_data (uii, "Could not find %s database...\r\n", name);
Delete (name);
return (-1);
}
Delete (name);
irr_object = New (irr_object_t);
irr_object->type = ROUTE;
irr_object->name = prefix_toax (prefix);
irr_object->origin = as;
if ((irr_object = load_irr_object (db, irr_object)) == NULL) {
uii_send_data (uii, "Could not find find %s...\r\n", prefix_toax (prefix));
return (-1);
}
if (delete_irr_route (db, irr_object->name, irr_object))
uii_send_data (uii, "Deleted\r\n");
else
uii_send_data (uii, "Deleted failed!\r\n");
return (1);
}
int uii_read_update_file (uii_connection_t *uii, char *file, char *name) {
irr_database_t *db;
u_long new;
FILE *fp;
db = find_database (name);
if (db == NULL) {
uii_send_data (uii, "Could not find %s database...\r\n", name);
Delete (name);
return (-1);
}
Delete (name);
if ((fp = fopen (file, "r")) == NULL) {
uii_send_data (uii, "Could not open %s\r\n", file);
return (-1);
}
/* just kill of the start line */
valid_start_line (db, fp, &new);
irr_update_lock (db);
if (scan_irr_file (db, "load", 2, fp) != NULL) {
trace (NORM, default_trace,
"Read of updates failed: serial number unchanged: %d\n",
db->serial_number);
}
irr_update_unlock (db);
fclose (fp);
return (1);
}
void run_cmd (char *cmd, FILE **in, FILE **out) {
int pin[2], pout[2];
int pid;
int omask, pstat;
pid_t ppid;
extern int errno;
if (in != NULL)
*in = NULL;
if (out != NULL)
*out = NULL;
if (in != NULL)
pipe (pin);
if (out != NULL)
pipe (pout);
/* if (fork() == 0) { */
pid = fork();
if (pid == 0) { /* We're the child */
if (in != NULL) {
close (pin[1]);
dup2 (pin[0], 0);
close (pin[0]);
}
if (out != NULL) {
close (pout[0]);
dup2 (pout[1], 1);
dup2 (pout[1], 2);
close (pout[1]);
}
execlp("/bin/sh", "sh", "-c", cmd, NULL);
_exit(127);
}
/* Only get here if we're the parent */
if (out != NULL) {
close (pout[1]);
*out = fdopen (pout[0], "r");
}
if (in != NULL) {
close (pin[0]);
*in = fdopen (pin[1], "w");
}
omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
do {
ppid = waitpid(pid, (int *) &pstat, 0);
} while (pid == -1 && errno == EINTR);
(void)sigsetmask(omask);
}
int kill_irrd (uii_connection_t *uii) {
uii_send_data (uii, "Are you sure (yes/no)? ");
if (uii_yes_no (uii)) {
uii_send_data (uii, "Exit requested\n");
exit(0);
/* XXX this code does not work -ljb */
/* need to mutex_init(irr->lock_all_mutex_lock) ? */
#ifdef notdef
irr = New (irr_connection_t);
irr->ll_database = IRR.ll_database;
irr_lock_all (irr);
mrt_set_force_exit (MRT_FORCE_EXIT);
#endif
}
return (0);
}
syntax highlighted by Code2HTML, v. 0.9.1