Skip to content

how to access global variable within the command function? #67

Open

Description

@HyunjunAhn

Hi. I have a question while testing the library. I would like to know how to access global variables within the command function registered through the cli_register_command().

If the value of the global variable changed inside main() is printed using cli_print() in the command function, it is not reflected, and on the contrary, the value of the global variable changed inside the command function is not properly displayed in main().
Also, is there a way to telnet output using cli_print() inside another function not registered through cli_register_command()?

Do you ever have a similar problem?

Activity

  1. RobSanders commented on Jan 19, 2021

    @RobSanders
    Collaborator

    Do you have a short example? In our code we do not access libcli globals in our callbacks, except for those things that are held in the cli_struct* that is passed to us.

  2. HyunjunAhn commented on Jan 19, 2021

    @HyunjunAhn
    Author

    Do you have a short example? In our code we do not access libcli globals in our callbacks, except for those things that are held in the cli_struct* that is passed to us.

    I would like to apply the CLI with minimal changes to our heavy application that has already been developed. Therefore, handling of external global variables is inevitable. I need to be able to change external global variables in the CLI, but the test code I wrote doesn't do that. Here is the test code:

      #include <limits.h>
      #include <stdio.h>
      #include <sys/types.h>
      #include <arpa/inet.h>
      #include <netinet/in.h>
      #include <sys/socket.h>
      #include <signal.h>
      #include <stdlib.h>
      #include <string.h>
      #include <unistd.h>
      #include <pthread.h>
    
      #include "libcli.h"
    
      #define CLITEST_PORT 8000
      #define IDLE_TIMEOUT 3600
      #define UNUSED(d) d __attribute__((unused))
    
    
      int gflag = 0;
    
      int flag_on(struct cli_def *cli, UNUSED(const char *command), char *argv[], int argc) {
        gflag = 1;
        cli_print(cli, "flag_on!");
        cli_print(cli, "gflag: %d", gflag);
    
        return CLI_OK;
      }
    
    
      int flag_off(struct cli_def *cli, UNUSED(const char *command), char *argv[], int argc) {
        gflag = 0;
        cli_print(cli, "flag_off!");
        cli_print(cli, "gflag: %d", gflag);
    
        return CLI_OK;
      }
    
      void run_child(int x) {
        struct cli_command *c;
        struct cli_def *cli;
        struct cli_optarg *o;
    
        cli = cli_init();
        cli_set_banner(cli, "libcli test");
        cli_set_hostname(cli, "hostname");
        cli_telnet_protocol(cli, 1);
    
        cli_register_command(cli, NULL, "on",  flag_on, PRIVILEGE_UNPRIVILEGED, MODE_EXEC,  NULL );
        cli_register_command(cli, NULL, "off", flag_off, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL );
    
        cli_loop(cli, x);
        cli_done(cli);
      }
    
      void *CLI_Thread() {
        int s, x;
        struct sockaddr_in addr;
        int on = 1;
    
        signal(SIGCHLD, SIG_IGN);
    
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
          perror("socket");
        }
    
        if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
          perror("setsockopt");
        }
    
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port = htons(CLITEST_PORT);
        if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
          perror("bind");
        }
    
        if (listen(s, 50) < 0) {
          perror("listen");
        }
    
        printf("Listening on port %d\n", CLITEST_PORT);
        while ((x = accept(s, NULL, 0))) {
          int pid = fork();
          if (pid < 0) {
            perror("fork");
          }
    
          /* parent */
          if (pid > 0) {
            socklen_t len = sizeof(addr);
            if (getpeername(x, (struct sockaddr *)&addr, &len) >= 0)
              printf(" * accepted connection from %s\n", inet_ntoa(addr.sin_addr));
    
            close(x);
            continue;
          }
    
          /* child */
          close(s);
          run_child(x);
          exit(0);
        }
      }
    
      void *TEST_Thread(void *arg){
          while(1){
              printf("gflag: %d\n", gflag);
              sleep(1);
          }
      }
    
      int main(){
    
        pthread_t thID[2];
        int c;
        
        pthread_create(&thID[0], NULL, CLI_Thread, NULL);
        pthread_create(&thID[1], NULL, TEST_Thread, NULL);
        
        while(1){
          c = getchar() - 48;
          if(c == 0) gflag = 0;
          else if(c == 1) gflag = 1;
        }
        return 0;
      }
    
  3. RobSanders commented on Jan 19, 2021

    @RobSanders
    Collaborator

    Think about what you are doing. In your main function you create one thread to monitor connections (CLI_Thread), and a second thread to periodically show the value of gflag. Both of these threads run in the same process. Now in CLI_Thread you wait for new connections, and fork each time you get one. So the child process is now handling the connection to the other end of the telnet sessions while the parent goes back to waiting for new connections. The fork() call creates a complete duplicate of the current process, but with a new PID. But unless you've taken steps to share variables the parent and child processes have their own separate copy of them. This means that when the child get a 'on' command to turn the flag on it goes and changes the copy of 'gflag' in its own process, but does not affect the copy of 'gflag' in the parent process.

  4. HyunjunAhn commented on Jan 20, 2021

    @HyunjunAhn
    Author

    Think about what you are doing. In your main function you create one thread to monitor connections (CLI_Thread), and a second thread to periodically show the value of gflag. Both of these threads run in the same process. Now in CLI_Thread you wait for new connections, and fork each time you get one. So the child process is now handling the connection to the other end of the telnet sessions while the parent goes back to waiting for new connections. The fork() call creates a complete duplicate of the current process, but with a new PID. But unless you've taken steps to share variables the parent and child processes have their own separate copy of them. This means that when the child get a 'on' command to turn the flag on it goes and changes the copy of 'gflag' in its own process, but does not affect the copy of 'gflag' in the parent process.

    Thanks for the answer. I will look for ways to share variables between child and parent processes. Maybe before that, can you tell me what are some of the actions to share the variable?

  5. RobSanders commented on Jan 20, 2021

    @RobSanders
    Collaborator

    Several different methods - shared memory is one of them for example. Much depends on your application and what it is trying to do.

  6. pengtianabc commented on Aug 12, 2024

    @pengtianabc

    Hi, @RobSanders :
    Like this issue, how could i use libcli a manager thread to manage a process. I want to add a console manager interface for a process, it maybe need a api like cli_loop can return EAGAIN, and NOT run in fork mode, so that i can execute some command in same process space.
    Or libcli is not design for this?

  7. RobSanders commented on Aug 12, 2024

    @RobSanders
    Collaborator

    Libcli was initially designed to imitate a Cisco style IOS interface, waiting for the user to enter one command, executing it, returning any results, and then waiting for the next one. This is how our apps treat it. Basically a single thread of execution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions