Multi-line system call in xcode

Discussion in 'Mac Programming' started by noahgolm, Jan 6, 2011.

  1. noahgolm macrumors member

    Joined:
    Aug 18, 2010
    #1
    Hi, I have a slight problem here. I am planning to develop a simple app (with a GUI) where a user can enter an ip address, and the computer tells if it is up or not. I am planning to use the system function here. I already have a bash script which accomplishes this, but I wonder how I can use it if system() only takes one-line arguments. Here's the bash script:
    Code:
    #!/bin/bash
    echo "Enter an IP address: "
    read ip
    if ! ping -c 1 -W 5 "$ip" &>/dev/null ; then
    echo "$ip is down"
    else
    echo "$ip is up"
    fi
    
    How can I put this in an objective-c app?
    Thank you!
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    There's really only one command you need to run, the ping. Test the result of system, and act accordingly.

    -Lee
     
  3. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #3
    You really are only running a single line
    Code:
    ping -c 1 -W 5 "$ip"
    the rest is wrapper code you should implement in Objective-C.

    EDIT: lee1210 beat me to it.

    B
     
  4. noahgolm thread starter macrumors member

    Joined:
    Aug 18, 2010
    #4
    Thanks everyone for the help so far. I've managed to get it to the point of a C program (to test the code), which can ping and display the result, but I don't know how to make the program actually test whether it's up (as in my "if [ping is successful] then [echo host is up]").
    In my bash, it accomplishes this with the "if ! ping -c 1 -W 5 "$ip" &>/dev/null" line, followed by the "then" statement. How can I recreate this, if I may ask?
     
  5. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #5
    Use the return code (exit status) of the ping, it's zero if up, non-zero if down. That's what the shell script is using.

    B
     
  6. noahgolm thread starter macrumors member

    Joined:
    Aug 18, 2010
    #6
    Thanks! I got it! I just incorporated the entire "if" statement into one line, which I'm not used to. I did this:
    Code:
    if ping -c 1 -W 5 %s &>/dev/null ; then echo "host is up"; else echo "host is down" ; fi
     
  7. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    =( That is not so good.

    Code:
    if(system(pingGoesHere) == 0) {
      printf("Host is up\n");
    } else {
      printf("Host is down\n");
    }
    
    Doing it like this allows you to do whatever you want with the result. Your version just gets some text in stdout, which is inflexible.

    -Lee
     
  8. noahgolm thread starter macrumors member

    Joined:
    Aug 18, 2010
    #8
    THe problem here is that I can't seem to use a C variable in the system() function! In my pinging, I use system("ping -c 1 -W 5 %s &>/dev/null", ip) but it cannot read this! Is there any way to resolve this? And I still thank you for the MUCH better method for the if statement.
     
  9. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #9
    1. What did you expect to happen?
    2. What actually did happen?

    If you're expecting system() to act like sprintf(), you are deeply mistaken.

    Break it down. First build a string that contains the ip address, then pass that to system().

    There are any number of ways to build one string from several parts. I recommend looking at the printf() family of functions, particularly snprintf(). I assume you know how to use man pages. If not, see Xcode's Help menu, choose Open man Page, enter snprintf, then read the resulting man page.

    What is a man page?
    http://en.wikipedia.org/wiki/Man_page

    You could also google the search terms: what is snprintf
     
  10. noahgolm thread starter macrumors member

    Joined:
    Aug 18, 2010
    #10
    I don't expect it to act like sprintf, and I have tried my own things, such as...
    Code:
    char command[512];
    sprintf(command, "my command with %s", ip);
    if(system(command) == 0){
    }
    
    However, this returns a segmentation fault error.
     
  11. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #11
    I was being lazy because I don't like to type code on my phone and also prefer that I can compile/test code... But I'll give it a whirl:
    Code:
    #include <stdio.h>
    int main(int argc, char *argv) {
      char *myIP = "10.1.2.3";
      char myCommand[100];
      int result = 0;
      myCommand[99]='\0';
      snprintf(myCommand,sizeof(myCommand),"ping -c 1 -W 5 %s",myIP);
      if(myCommand[99] != 0) {
        fprintf(stderr,"IP is too long for command buffer.\n");
        return -1;
      }
      result = system(myCommand);
      if(result == 0) {
        printf("Host is up!\n");
      } else if (result > 0 && result != 127) {
        printf("Host is down!\n");
      } else {
        fprintf(stderr,"Could not run ping command.\n");
        return -2;
      }
      return 0;
    }
    
    Not tested, but something like that should do it.

    -Lee
     
  12. noahgolm thread starter macrumors member

    Joined:
    Aug 18, 2010
    #12
    Wow, that seems to work great! Only problem is that once I add the scanf part, which is scanf("%s", myIP), it goes back with the errors again (same as before...). Am I using the string properly and allocation properly?
     
  13. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #13
    I have absolutely no idea, because I can't see your code. I can hazard a guess and say that you are not using some string correctly if you are getting a crash. What does (all of) your code look like? Where does it crash?

    -Lee

    Edit: if you just added that line to my code, bad things will happen. I initialized myIP using a string literal. If you try to write over that nothing good will happen. That is not your memory to crush. You'll need a buffer allocated for your read that is big enough for a huge amount of input. A valid IP can only be 15 characters, but nothing forces someone to enter a valid IP.
     
  14. noahgolm thread starter macrumors member

    Joined:
    Aug 18, 2010
    #14
    Here's the program:
    Code:
    #include <stdio.h>
    int main(int argc, char *argv) {
    	printf("Please enter an IP address:");
    	char *myIP;
    	scanf("%s", myIP);
    	char myCommand[100];
    	int result = 0;
    	myCommand[99]='\0';
    	snprintf(myCommand,sizeof(myCommand),"ping -c 1 -W 5 %s &>/dev/null",myIP);
    	if(myCommand[99] != 0) {
    		fprintf(stderr,"IP is too long for command buffer.\n");
    		return -1;
    	}
    	result = system(myCommand);
    	if(result == 0) {
    		printf("Host is up!\n");
    	} else if (result > 0 && result != 127) {
    		printf("Host is down!\n");
    	} else {
    		fprintf(stderr,"Could not run ping command.\n");
    		return -2;
    	}
    	return 0;
    }
    Once I enter an ip address and press return, it tells me that there is a bus error, then exits the program. I'm guessing there's something wrong here with how I'm allocating the myIP variable, right?
     
  15. lee1210, Jan 6, 2011
    Last edited: Jan 6, 2011

    lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #15
    There is something fundamentally wrong with using myIP this way. It is uninitialized. It could point anywhere in memory. If it points to something you have permission to change, it's still going to overwrite something. There is a better chance that it points out into the aether, and you're trying to plunk some input into an area you have no claim to. You need to statically allocate a large char [] for your input, or assign the result of malloc with a large size to myIP then free this when you're done.

    scanf is dangerous (EDIT: I meant with %s) because you can't limit the input length, but for this test you can get away with it as long as you pass a pointer to a buffer you own.

    -Lee

    Edit: you should be able to fix this by making myIP:
    char myIP[1000];

    I should have checked result explicitly against -1 not > 0, but ping returns 0, 1, or 2, so it should be OK.
     
  16. noahgolm thread starter macrumors member

    Joined:
    Aug 18, 2010
    #16
    Thank you very much for this help! I'm sorry, I have not programmed in C in quite some time. I resolved the issue by adding a buffer and slightly changing the definition of myIP. The code where scanf is now looks like this:
    Code:
    char myIP[30] = {'\0}'};
    scanf("%29s", myIP);
    Thank you!
     
  17. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #17
    You should really spend a little time reading man pages.

    snprintf() returns an int. It's the count of how many chars it would have written if the size were infinite. Since snprintf() will NOT run past the given size, and WILL always nul-terminate a buffer, the if(myCommand[99] != 0) will NEVER be true. All this is very concisely described in the man page.
     
  18. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #18
    If this is part of code you intend to distribute, implement some additional input verification.

    Fortunately your system doesn't require privileged access, but you should really be checking for anything that doesn't look like an IP in the input string. Make sure it only uses digits and periods, or alphanumerics if you are allowing hostnames as well.

    Read the appropriate RFC if necessary or find an existing function or library that will help.

    B
     
  19. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #19
    That was 100% my fault, that was code I provided. I didn't look that part up, and for whatever reason thought if the result was greater than size as much as possible would be written without null-termination. Mea culpa.

    -Lee
     

Share This Page