openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

bcm-diag-proxy.c (19598B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  */
      7 
      8 #include <stdlib.h>
      9 #include <stdio.h>
     10 #include <string.h>
     11 #include <unistd.h>
     12 #include <assert.h>
     13 #include <signal.h>
     14 
     15 #include <sal/core/thread.h>
     16 #include <sal/appl/config.h>
     17 #include <linux-uk-proxy.h>
     18 
     19 #include "proxy-service.h"
     20 #include "socintf.h"
     21 
     22 /*
     23  * This is the proxy service the kernel diag shell uses. 
     24  */
     25 static char _input_proxy_service[64]    = "BCM DIAG SHELL";
     26 static char _output_proxy_service[64]   = "BCM DIAG SHELL";
     27 
     28 #define OUTPUT_LINE(_ctrl, _buf) \
     29 do { \
     30     if (_ctrl->ctrl.output_cb) { \
     31         unsigned int _len = strlen(_buf)+1; \
     32         _ctrl->ctrl.output_cb((proxy_ctrl_t*)_ctrl, (unsigned char*)_buf, &_len); \
     33     } \
     34 } while (0)
     35 
     36 #define COPY_AND_OUTPUT_CMD(_ctrl, _buf, _cmd) \
     37 do { \
     38     strcpy(_buf, _cmd); \
     39     strcat(_buf, "\n"); \
     40     OUTPUT_LINE(_ctrl, _buf); \
     41 } while (0)
     42 
     43 #define LINE_BUF_SIZE 256
     44 
     45 #ifdef INCLUDE_EDITLINE
     46 extern char *readline(const char *prompt);
     47 extern void add_history(char *p);
     48 #endif
     49 static int use_editline = 0;
     50 
     51 static char *progname;
     52 
     53 /*
     54  * Proxy control structure passed to all of our callbacks. 
     55  */
     56 typedef struct proxy_ctrl_t {
     57     proxy_ctrl_t ctrl;
     58     
     59     const char *service;        /* Service pointer for kernel proxy callbacks */
     60 
     61     int output_fd;              /* File descriptor for the output callback */
     62     int input_fd;               /* File descriptor for the input callback */
     63 
     64     proxy_ctrl_t *sibling;      /* Other proxies which should stop when this one does */
     65 } our_proxy_ctrl_t;
     66     
     67     
     68 /*
     69  * Function: skip_whitespace
     70  *
     71  * Purpose:
     72  *      Skip over leading whitespace in string
     73  * Parameters:
     74  *      s - string to parse
     75  * Returns:
     76  *      pointer to first non-whitespace character in input string
     77  */ 
     78 static char *
     79 skip_whitespace(char *s)
     80 { 
     81     /* Skip leading whitespace */
     82     while (*s && *s <= ' ') {
     83         s++;
     84     }
     85     return s;
     86 }
     87 
     88 /*
     89  * Function: load_config
     90  *
     91  * Purpose:
     92  *    Load BCM configuration file
     93  * Parameters:
     94  *    ctrl - proxy service control structure
     95  *    fname - name of configuration file
     96  * Returns:
     97  *    0 - configuration file found and loaded
     98  *    -1 - configuration file not found
     99  */ 
    100 static int
    101 load_config(our_proxy_ctrl_t *ctrl, 
    102             char *fname)
    103 {
    104     char cmd[64];
    105     char line[LINE_BUF_SIZE];
    106     FILE *fp;
    107     int offs;;
    108 
    109     fp = fopen(fname, "r");
    110     if (!fp) {
    111         printf("cannot open '%s'\n", fname);
    112         return -1;
    113     }
    114     COPY_AND_OUTPUT_CMD(ctrl, cmd, "local proxy_cmd_ enter_batchmode");
    115     line[LINE_BUF_SIZE-1] = 0;
    116     strcpy(line, "config add ");
    117     offs = strlen(line);
    118     while (fgets(&line[offs], LINE_BUF_SIZE-1-offs, fp)) {
    119         if (line[offs] != '#' && strchr(&line[offs], '=') != NULL) {
    120             OUTPUT_LINE(ctrl, line);
    121         }
    122     }
    123     COPY_AND_OUTPUT_CMD(ctrl, cmd, "local proxy_cmd_ exit_batchmode");
    124     fclose(fp);
    125     return 0;
    126 }
    127 
    128 /*
    129  * Function: run_script
    130  *
    131  * Purpose:
    132  *    Run soc script by passing file content through proxy
    133  * Parameters:
    134  *    ctrl - proxy service control structure
    135  *    script - name of script file to run
    136  * Returns:
    137  *    0 - script found and executed
    138  *    -1 - script not found
    139  */ 
    140 static int
    141 run_script(our_proxy_ctrl_t *ctrl, 
    142            char *script,
    143            char *init_script,
    144            int recursion_level)
    145 {
    146     char line[LINE_BUF_SIZE];
    147     char cmd[64];
    148     char *s;
    149     FILE *fp;
    150     int cont = 0;
    151     int reinit = 0;
    152     int offset = 0;
    153 
    154     if (++recursion_level > 4) {
    155         printf("recursion too deep to run script '%s'\n", script);
    156         return -1;
    157     }
    158     fp = fopen(script, "r");
    159     if (!fp) {
    160 	if(strchr(script, '$')) {
    161 	    sprintf(line, "rcload %s", script); 
    162 	    COPY_AND_OUTPUT_CMD(ctrl, cmd, line);
    163 	    sal_usleep(500000); 
    164 	    return 0; 
    165 	}
    166 	else {
    167 	    printf("cannot open script '%s'\n", script);
    168 	    return -1;
    169 	}
    170     }
    171     if (recursion_level == 1) {
    172         COPY_AND_OUTPUT_CMD(ctrl, cmd, "local proxy_cmd_ enter_batchmode");
    173     }
    174     COPY_AND_OUTPUT_CMD(ctrl, cmd, "local proxy_cmd_ push_scope");
    175     line[LINE_BUF_SIZE-1] = 0;
    176     while (fgets(line, LINE_BUF_SIZE-1, fp)) {
    177         s = skip_whitespace(line);
    178         if (!strncmp(line, "rcload ", 7)) {
    179             if ((s = strchr(line, '\n')) != NULL) {
    180                 *s = 0;
    181             }
    182             s = skip_whitespace(line+7);
    183             run_script(ctrl, s, init_script, recursion_level);
    184         }
    185         if (init_script && 
    186             (!strncmp(s, "tr ", 3) || !strncmp(s, "testrun ", 8))) {
    187             /* 
    188              * Call init script after each testrun. Ideally we would
    189              * run this script *before* the testrun command, but we may
    190              * be in the middle of a multi-line command.
    191              */
    192             reinit = 1;
    193             /*
    194              * Exceptions to speed up sanity tests
    195              * Skip rc.soc for a few tests that don't have TRC flag set
    196              * to prevent linkscan task from getting enabled. 
    197              */
    198             offset = 0;
    199             if (!strncmp(s, "tr ", 3)) {
    200                 offset = 3;
    201             } else if (!strncmp(s, "testrun ", 8)) {
    202                 offset = 8;
    203             }
    204             if (((s[offset] >= '1') && (s[offset] <= '8') &&
    205                  ((s[offset + 1] < '0') || (s[offset + 1] > '9'))) ||
    206                 !strncmp(&s[offset], "15", 2) ||
    207                 !strncmp(&s[offset], "16", 2) ||
    208                 !strncmp(&s[offset], "21", 2) ||
    209                 !strncmp(&s[offset], "30", 2) ||
    210                 !strncmp(&s[offset], "31", 2) ||
    211                 !strncmp(&s[offset], "32", 2) ||
    212                 !strncmp(&s[offset], "33", 2) ||
    213                 !strncmp(&s[offset], "34", 2) ||
    214                 !strncmp(&s[offset], "35", 2) ||
    215                 !strncmp(&s[offset], "36", 2) ||
    216                 !strncmp(&s[offset], "37", 2) ||
    217                 !strncmp(&s[offset], "38", 2) ||
    218                 !strncmp(&s[offset], "50", 2) ||
    219                 !strncmp(&s[offset], "51", 2) ||
    220                 !strncmp(&s[offset], "52", 2) ||
    221                 !strncmp(&s[offset], "71", 2) ||
    222                 !strncmp(&s[offset], "85", 2) ||
    223                 !strncmp(&s[offset], "86", 2) ||
    224                 !strncmp(&s[offset], "87", 2) ||
    225                 !strncmp(&s[offset], "88", 2)
    226                 ) {
    227                 reinit = 0;
    228             }
    229         }
    230         if (*s != '#' && strncmp(s, "rccache", 7) != 0) {
    231             /* Send the line directly to output callback */
    232             OUTPUT_LINE(ctrl, line);
    233         }
    234         /* Check for line continuation */
    235         cont = (strstr(line, "\\\n")) ? 1 : 0;
    236         if (reinit && !cont) {
    237             /* 
    238              * Run init script as soon as possible after completing
    239              * the testrun command. Since command line concatenation
    240              * is handled remotely, we need to wait until we have sent
    241              * a complete command line.
    242              */
    243             COPY_AND_OUTPUT_CMD(ctrl, cmd, "local proxy_cmd_ push_scope");
    244             COPY_AND_OUTPUT_CMD(ctrl, cmd, "set rctest=0");
    245             run_script(ctrl, init_script, NULL, recursion_level);
    246             COPY_AND_OUTPUT_CMD(ctrl, cmd, "local proxy_cmd_ pop_scope");
    247             reinit = 0;
    248         }
    249     }
    250     COPY_AND_OUTPUT_CMD(ctrl, cmd, "local proxy_cmd_ pop_scope");
    251     if (recursion_level == 1) {
    252         COPY_AND_OUTPUT_CMD(ctrl, cmd, "local proxy_cmd_ exit_batchmode");
    253     }
    254     fclose(fp);
    255     return 0;
    256 }
    257 
    258 /*
    259  * Function: _fd_input_cb
    260  *
    261  * Purpose:
    262  *    Proxy input function from a file descriptor
    263  * Parameters:
    264  *    ctrl - Proxy service control structure
    265  *    data - (OUT) buffer to receive the data
    266  *    len - (OUT) receives the data length
    267  * Returns:
    268  *    0 - data is ready to send.
    269  *    -1 - do not send data any data.
    270  * Notes:
    271  *    It reads commands from the descriptor.
    272  *    The following commands are interpreted locally:
    273  *
    274  *    quit - Quit the proxy program.
    275  *       Any data AFTER the quit is sent to the remote. Thus,
    276  *       "quit exit" will quit the local, and send "exit" to 
    277  *       the remote. "quit" by itself will quit the local, and 
    278  *       send nothing to the remote. This functionality prevents 
    279  *       the user from accidentally exiting the diag shell at 
    280  *       the remote end.
    281  *
    282  *    exit - Quit the proxy program.
    283  *       Same functionality as the quit command.
    284  *
    285  *    rcload <file>
    286  *       Lines in file <file> will be sent to the remote end.
    287  *       This provides a rudimentary approximation of the real
    288  *       diag shell 'rcload' function. 
    289  */
    290 
    291 
    292 /*
    293  * Sometimes an input line (an instruction from the kernel shell) 
    294  * needs to be injected into the input read buffer. 
    295  */
    296 static char _proxy_input_line[64]; 
    297 
    298 static int
    299 _fd_input_cb(our_proxy_ctrl_t *ctrl, 
    300              unsigned char *udata, 
    301              unsigned int *len)
    302 {
    303     char *config_file;
    304     char *init_script;
    305     char *s = NULL;
    306     char *data = (char *)udata;
    307     FILE *fp;
    308     int input_fd;
    309     
    310     /* Check for locally specified input first */
    311     if(_proxy_input_line[0]) {
    312 	strcpy(data, _proxy_input_line); 
    313 	_proxy_input_line[0] = 0;
    314     } else {
    315 	/* Retrieve input from the file descriptor */
    316 #ifdef INCLUDE_EDITLINE
    317     if (use_editline) {
    318 	    s = readline("");
    319         if (s) {
    320             add_history(s);
    321             strncpy(data, s, *len-1);
    322             data[*len-1] = 0;
    323             strcat(data, "\n");
    324         } else {
    325             strcpy(data, "quit\n");
    326         }
    327     }
    328 #endif
    329 	if (!use_editline) {
    330         /*
    331          * Duplicate file descriptor when using fdopen() to
    332          * prevent underlying socket from being closed by
    333          * fclose().
    334          */
    335         input_fd = dup(ctrl->input_fd);
    336         if (input_fd < 0) {
    337             printf("failed to dup input_fd %d\n", ctrl->input_fd);
    338             return -1;
    339         }
    340 
    341         /* Get a line from the descriptor */
    342         fp = fdopen(input_fd, "r");
    343 	    assert(fp);
    344 	    if (fgets(data, *len, fp) == NULL) {
    345 		/* Broken pipe. Lets quit */
    346             strcpy(data, "quit\n");
    347 	    }
    348 	    /* Strip CR characters (e.g. from telnet) */
    349 	    if ((s = strchr(data, '\r')) != NULL) {
    350 		*s++ = '\n';
    351 		*s = 0;
    352 	    }
    353 	    /* Support backspace */
    354 	    while ((s = strchr(data, 0x08)) != NULL) {
    355 		if (s == (char *)data) {
    356 		    memcpy(s, &s[1], strlen(s));
    357 		} else {
    358 		    memcpy(&s[-1], &s[1], strlen(s));
    359 		}
    360 	    }
    361             fclose(fp);
    362 	}
    363     }
    364 
    365     /* Special commands? */
    366     if (!strncmp(data, "quit", 4) || !strncmp(data, "exit", 4)) {
    367         /* Tell our service thread to exit */
    368         /* This will happen AFTER the data result from this function is sent. */
    369         ctrl->ctrl.exit = 1;
    370 	    return -1;
    371     }
    372     
    373     if (!strncmp(data, "rcload ", 7)) {
    374         /* Read lines from the given file */
    375         if ((s = strchr(data, '\n')) != NULL) {
    376             *s = 0;
    377         }
    378         s = skip_whitespace(data+7);
    379         init_script = "rc.soc";
    380         if (!strncmp(s, "-i", 2)) {
    381             /* 
    382              * For backwards compatibility we need to silently
    383              * ignore the -i switch, which used to be required
    384              * for rc.soc to be executed in between testruns.
    385              */
    386             s = skip_whitespace(s+2);
    387         } else if (!strncmp(s, "-i-", 3)) {
    388             /*
    389              * Turn off init script feature (normally not used).
    390              */
    391             init_script = NULL;
    392             s = skip_whitespace(s+3);
    393         }
    394         run_script(ctrl, s, init_script, 0);
    395         return -1;
    396     }
    397     
    398     if (!strncmp(data, "config refresh", 14)) {
    399         /* Read config.bcm */
    400         if ((config_file = getenv("BCM_CONFIG_FILE")) == NULL) {
    401             config_file = SAL_CONFIG_FILE;
    402         }
    403         load_config(ctrl, config_file);
    404         return -1;
    405     }
    406     
    407     *len = strlen(data) + 1;
    408 
    409     /* Send this buffer */
    410     return 0;
    411 }
    412 
    413 /*
    414  * Function: _fd_output_cb
    415  *
    416  * Purpose:
    417  *    Proxy output function to a file descriptor
    418  * Parameters:
    419  *    ctrl - proxy service control structure
    420  *    data - data which was read from the proxy
    421  *    len - length of the data. 
    422  * Returns:
    423  *    Always 0
    424  * Notes:
    425  *    This is an output callback for the proxy service. 
    426  */ 
    427 static int
    428 _fd_output_cb(our_proxy_ctrl_t *ctrl, 
    429               unsigned char *udata, 
    430               unsigned int *len)
    431 {    
    432     char *data = (char *)udata;
    433 
    434     /* Do we have an instruction from the shell to execute? */
    435     if(!strncmp("proxyecho", data, 9)) {
    436 	strcpy(_proxy_input_line, data+10); 
    437     }
    438     else {
    439 	write(ctrl->output_fd, data, *len);
    440 	fsync(ctrl->output_fd);
    441     }
    442     return 0;
    443 }
    444 
    445 
    446 /*
    447  * Function: _kernel_proxy_output_cb
    448  *
    449  * Purpose:
    450  *    Proxy output function. 
    451  * Parameters:
    452  *    ctrl - proxy control structure
    453  *    data - output data
    454  *    len - length of data
    455  * Returns:
    456  *    Always 0
    457  */
    458 static int
    459 _kernel_proxy_output_cb(our_proxy_ctrl_t *ctrl, 
    460                         unsigned char *data, 
    461                         unsigned int *len)
    462 {
    463     /* This data should be sent to the given proxy service */
    464     return linux_uk_proxy_send(ctrl->service, data, *len);
    465 }
    466 
    467 /*
    468  * Function: _kernel_proxy_input_cb
    469  *
    470  * Purpose:
    471  *    Proxy input function
    472  * Parameters:
    473  *    ctrl - proxy control structure
    474  *    data - (OUT) proxy data
    475  *    len - (OUT) length of the data
    476  * Returns:
    477  *    0 - data available
    478  *    -1 - no data 
    479  */
    480 static int
    481 _kernel_proxy_input_cb(our_proxy_ctrl_t *ctrl, 
    482                        unsigned char *data, 
    483                        unsigned int *len)
    484 {
    485     return linux_uk_proxy_recv(ctrl->service, data, len);
    486 }
    487 
    488 
    489 /*
    490  * Function: _kernel_proxy_input_exit_cb
    491  *
    492  * Purpose:
    493  *    To be called when input proxy exits
    494  * Parameters:
    495  *    ctrl - proxy control structure
    496  *    data - unused
    497  *    len - unused
    498  * Returns:
    499  *    0 - data available
    500  *    -1 - no data 
    501  */
    502 static int
    503 _kernel_proxy_input_exit_cb(our_proxy_ctrl_t *ctrl, 
    504                             unsigned char *data, 
    505                             unsigned int *len)
    506 {
    507     /* Suspend service to prevent kernel threads from blocking */
    508     linux_uk_proxy_suspend(_input_proxy_service);
    509     return 0;
    510 }
    511 
    512 static void
    513 _trigger_read_proxy_thread_exit(proxy_ctrl_t* ctrl)
    514 {
    515     unsigned char data[] = {'\n', '\0'}; 
    516     unsigned int len = 1;
    517 
    518 	if (ctrl->output_cb) {
    519 		ctrl->output_cb(ctrl, data, &len); 
    520 	}
    521 }
    522 
    523 /*
    524  * Function: _kernel_fd_attach
    525  *
    526  * Purpose:
    527  *    Attach I/O file descriptors to proxy service
    528  * Parameters:
    529  *    input_fd - file descriptor for input callback
    530  *    output_fd - file descriptor for output callback
    531  * Returns:
    532  *    0
    533  */
    534 static int
    535 _kernel_fd_attach(int input_fd, int output_fd)
    536 {
    537     our_proxy_ctrl_t reader;
    538     our_proxy_ctrl_t writer;
    539 
    540     memset(&reader, 0, sizeof(reader));
    541     memset(&writer, 0, sizeof(writer));
    542 
    543     if (getenv("INPUT_PROXY")) {
    544         strcpy(_input_proxy_service, getenv("INPUT_PROXY"));
    545     }
    546     if (getenv("OUTPUT_PROXY")) {
    547         strcpy(_output_proxy_service, getenv("OUTPUT_PROXY"));
    548     }
    549 
    550     linux_uk_proxy_open();
    551 
    552     /* Create the service, if it is not yet available. */
    553     /* We configure a queue size of 1 -- we do not want the io buffered */
    554     linux_uk_proxy_service_create(_input_proxy_service, 1, 0);
    555 
    556     reader.ctrl.input_cb = (proxy_data_cb_t)_kernel_proxy_input_cb;
    557     reader.ctrl.output_cb = (proxy_data_cb_t)_fd_output_cb;
    558     reader.ctrl.exit_cb = (proxy_data_cb_t)_kernel_proxy_input_exit_cb;
    559     reader.ctrl.max_data_size = LUK_MAX_DATA_SIZE;
    560     reader.ctrl.exit = 0;
    561     
    562     reader.service = _input_proxy_service;
    563     reader.output_fd = output_fd;
    564     reader.sibling = NULL;
    565     
    566     /* Fork off the reader loop */
    567     proxy_service_start(&reader.ctrl, 1);
    568 
    569     /* Resume in case service was suspended */
    570     linux_uk_proxy_resume(_input_proxy_service);
    571 
    572     /* Create the service, if it is not yet available. */
    573     /* We configure a queue size of 1 -- we do not want the io buffered */
    574     linux_uk_proxy_service_create(_output_proxy_service, 1, 0);
    575 
    576     writer.ctrl.input_cb = (proxy_data_cb_t)_fd_input_cb;
    577     writer.ctrl.output_cb = (proxy_data_cb_t)_kernel_proxy_output_cb;
    578     writer.ctrl.exit_cb = NULL;
    579     writer.ctrl.max_data_size = LUK_MAX_DATA_SIZE;
    580     writer.ctrl.exit = 0;
    581     
    582     writer.service = _output_proxy_service;
    583     writer.input_fd = input_fd;
    584     writer.sibling = &reader.ctrl;
    585     
    586     /* Start the writer loop (don't fork) */
    587     proxy_service_start(&writer.ctrl, 0);
    588     /* Tell our sibling thread to exit as well */
    589     writer.sibling->exit = 2;
    590 
    591     /* Wait for sibling to exit */
    592     while (writer.sibling->exit) {
    593         sal_usleep(150000);
    594         if (writer.sibling->exit) {
    595              _trigger_read_proxy_thread_exit(&writer.ctrl);
    596         }
    597     }
    598 
    599     return 0;
    600 }
    601 
    602 static int
    603 _fd_stdio_attach(int fd)
    604 {
    605     our_proxy_ctrl_t reader;
    606     our_proxy_ctrl_t writer;
    607 
    608     memset(&reader, 0, sizeof(reader));
    609     memset(&writer, 0, sizeof(writer));
    610 
    611     reader.ctrl.input_cb = (proxy_data_cb_t)_fd_input_cb;
    612     reader.input_fd = fd;
    613     reader.ctrl.output_cb = (proxy_data_cb_t)_fd_output_cb;    
    614     reader.output_fd = 0;
    615     reader.ctrl.max_data_size = LUK_MAX_DATA_SIZE;
    616     reader.ctrl.exit = 0;    
    617     reader.sibling = NULL;
    618     reader.ctrl.exit_cb = NULL;
    619 
    620     /* Fork off the reader loop */
    621     proxy_service_start(&reader.ctrl, 1);
    622     
    623     writer.ctrl.input_cb = (proxy_data_cb_t)_fd_input_cb;
    624     writer.input_fd = 1;
    625     writer.ctrl.output_cb = (proxy_data_cb_t)_fd_output_cb;
    626     writer.output_fd = fd;
    627     writer.ctrl.max_data_size = LUK_MAX_DATA_SIZE;
    628     writer.ctrl.exit = 0;   
    629     writer.sibling = &reader.ctrl;
    630     writer.ctrl.exit_cb = NULL;
    631 
    632     /* Start the writer loop (don't fork) */
    633     proxy_service_start(&writer.ctrl, 0);
    634     writer.sibling->exit = 2;
    635     return 0;
    636 }
    637 
    638 static int
    639 _daemon(int port)
    640 {
    641     int server_s;
    642     int client_s;
    643 
    644     server_s = setup_socket(port);    
    645 
    646     for (;;) {
    647         printf("waiting for connections...\n");
    648         client_s = wait_for_cnxn(server_s);
    649         printf("client connected.\n");
    650         _kernel_fd_attach(client_s, client_s);
    651         close(client_s);
    652     }
    653     return 0;
    654 }
    655 
    656 static void 
    657 usage(void)
    658 {
    659     printf("Usage: %s [-d port]\n", progname);
    660     printf("       %s [-s hostname port]\n", progname);
    661 #ifdef INCLUDE_EDITLINE
    662     printf("       %s [-n]\n", progname);
    663 #endif
    664 }
    665 
    666 static void
    667 ctrl_c_handler(int sig)
    668 {
    669     /* Suspend service to prevent kernel threads from blocking */
    670     linux_uk_proxy_suspend(_input_proxy_service);
    671 
    672     exit(0);
    673 }
    674 
    675 int 
    676 main(int argc, char *argv[])
    677 {
    678     if ((progname = strrchr(argv[0], '/')) == NULL) {
    679         progname = argv[0];
    680     } else {
    681         progname++;
    682     }
    683 
    684     /* Suspend proxy on exit */
    685     signal(SIGINT, ctrl_c_handler);
    686 
    687     /* Broken pipes are not a problem */
    688     signal(SIGPIPE, SIG_IGN);
    689 
    690     if (argc == 1) {
    691         /* Attach on stdio */
    692 #ifdef INCLUDE_EDITLINE
    693         use_editline = 1;
    694 #else
    695         use_editline = 0;
    696 #endif
    697         _kernel_fd_attach(0, 1);
    698 #ifdef INCLUDE_EDITLINE
    699     } else if (!strcmp(argv[1], "-n") && argc == 2) {
    700         /* Attach on stdio */
    701         _kernel_fd_attach(0, 1);
    702 #endif
    703     } else if (!strcmp(argv[1], "-d") && argc == 3) {
    704         /* Port to listen on */
    705         int port = atoi(argv[2]);
    706         _daemon(port);
    707     } else if (!strcmp(argv[1], "-s") && argc == 4) {
    708         /* Connect to server */
    709         char *s = argv[2];
    710         int p = atoi(argv[3]);
    711         int sockfd = conn(s, p);
    712         _fd_stdio_attach(sockfd);
    713     } else {
    714         usage();
    715     }
    716     return 0;
    717 }