Thread: posix process handling

  1. #1
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,735

    posix process handling

    Before I continue with this on my own I just wanted to know if anyone could see some glaring issues or even just minor ones.
    Code:
    #include <stdint.h>
    #include <stdbool.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #ifdef _WIN32
    // omitted
    #else
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <sys/ptrace.h>
    typedef struct POSIX_PROC_
    {
        int pid;
        int imem;
        int omem;
        char path[6 + (CHAR_BIT * 2)];
    } POSIX_PROC;
    int posixFindProc( char const *name )
    {
        // Check we can find process
        DIR *dir = opendir("/proc");
        if ( !dir ) return 0;
        // Gonna need this in a min
        struct dirent *ent = NULL;
        char path[_MAX_PATH] = "/proc/";
        char line[_MAX_PATH] = "";
        FILE *file = NULL;
        while( ent = readdir(dir) )
        {
            if (!isdigit(*ent->d_name)) continue;
            path[6] = '\0';
            strcat(path, ent->d_name);
            strcat(path, "/stat");
            file = fopen(path, "r");
            if (!file) continue;
            fscanf( file, "%d", &pid );
            line[0] = '\0';
            fscanf( file, "%s", line );
            line[strlen(line)-1] = '\0';
            fclose(file);
            if ( strcmpi( &line[1], name ) == 0 ) return pid;
        }
        return 0;
    }
    POSIX_PROC posixOpenProc( int pid, int, bool )
    {
        POSIX_PROC proc = { pid, 0, "" };
        if ( pid <= 0 ) return proc;
        char mem[6 + (CHAR_BIT * 2) + 3 ] = {0};
        sprintf( cpid, "/proc/%d/", pid );
        strcat( proc.path, cpid );
        strcpy( mem, proc.path );
        strcat( mem, "mem");
        proc.imem = _open( mem, _O_READ  | _O_BINARY );
        proc.omem = _open( mem, _O_WRITE | _O_BINARY );
        return proc;
    }
    uintptr_t upseek( int fd, uintptr_t addr )
    {
        if ( _lseek( fd, 0, SEEK_SET ) <= 0 ) return 0;
        while ( addr > PTRDIFF_MAX )
        {
            if ( _lseek( fd, PTRDIFF_MAX, SEEK_CUR ) <=0 )
                return 0;
            addr -= PTRDIFF_MAX;
        }
        return (_lseek( fd, addr, SEEK_CUR ) > 0) ? 1 : 0;
    }
    size_t posixRdProc( POSIX_PROC proc, uintptr_t addr, void *buff, size_t size )
    {
        if ( proc.imem <= 0 || !upseek( proc.imem, addr )) return 0;
        return _read( proc.imem, buff, size );
    }
    size_t posixWrProc( POSIX_PROC proc, uintptr_t addr, void *buff, size_t size )
    {
        if ( proc.omem <= 0 )
        {
            long result = ptrace( PTRACE_POKE_DATA, proc.pid, addr, buff );
            if ( result == 0 )
                ptrace( PTRACE_POKE_TEXT, proc.pid, addr, buff );
            if ( result == 0 )
                ptrace( PTRACE_POKE_USER, proc.pid, addr, buff );
            return result;
        }
        if ( !upseek( proc.omem, addr ) ) return 0;
        return _write( proc.omem, buff, size );
    }
    POSIX_PROC posixShutProc( POSIX_PROC proc )
    {
        _close(proc.mem);
        return (POSIX_PROC){0,0,""};
    }
    bool   posixKillProc( int fdProc )
    {
        return 0;
    }
    #endif
    
    int main(void)
    {
        return 0;
    }

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,667
    Aside from the missing declarations which prevent it compiling (cpid, line 53)?

    > char path[6 + (CHAR_BIT * 2)];
    How does CHAR_BIT * 2 relate to the length of a printed decimal integer, or more specifically a pid_t ?

    > DIR *dir = opendir("/proc");
    You never call closedir()

    > path[6] = '\0';
    > strcat(path, ent->d_name);
    Perhaps
    strcpy( &path[6], ent->d_name );

    > if ( proc.imem <= 0 || !upseek( proc.imem, addr )) return 0;
    The standard for read fail is -1, not 0
    You can't tell the difference between a successful read of zero, or a failure.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,735
    Forgotten about this thread
    * strcpy, no because strcat was used during testing on linux mint and worked fine (code has been updated a little since my original post)
    * CHAR_BIT, that I had actually given some thought before hand, knowing INT_MAX is generally 2m odd I realised that CHAR_BIT was a decent match to the number of characters required, I multiplied it by 2 to provide room for the /cmdline part
    * since the linux docs state pid is to be read w/ %d and %d is supposed to be a signed integer I knew it was safe to only account for the INT_MAX limit (hence the CHAR_BIT abuse)
    * closedir has just now been added to the code so thank you for pointing that out
    * the read fail you'll have to re-read the code on because when you do you'll notice I'm returning the number of bytes read, if you're referring to the <= 0 then that's because I don't want to be using the std handles, still debating weather I should just put < 3 there instead

  4. #4
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,735
    I also have a enumProc function now as well, I'm planning to put these in a library for other developers to download later so if you think of any functions that should be added then please mention them, I enjoy the challenge.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by awsdert
    CHAR_BIT, that I had actually given some thought before hand, knowing INT_MAX is generally 2m odd I realised that CHAR_BIT was a decent match to the number of characters required, I multiplied it by 2 to provide room for the /cmdline part
    The problem is that CHAR_BIT gives the number of bits in a byte (in C terms). This is not related to the number of characters needed for a decimal string representation of an integer (the total number of bits for the size of the integer type is related, but one could theoretically have a larger CHAR_BIT and fewer bytes). In fact, where INT_MAX is 2147483647, the number of characters would be 10 (excluding the null character), whereas CHAR_BIT is likely to be 8. Consequently, it isn't actually better than just defining your own constant with the value of 22, and possibly worse since without an explanation it creates confusion.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Registered User
    Join Date
    Jun 2011
    Posts
    88
    the code at line 42 waste cpu cycles.
    line[strlen(line)-1] = '\0';
    strlen iterates through a string until it finds a string terminator ( aka '\0' )
    so the string line already has a string terminator where you are putting a string terminator

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Well, it does do something: reduce the string by one character, though exactly why that is done is not clear to me, and I would rather play it safe and both check the return value of fscanf and check that the string's length is greater than 0.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,735
    the shortening of the string was down to how the linux man pages explained the contents of the exe file (enclosed in a bracket variant, can't remember which), later however I discovered this was not necessarily a match for the file that the process was launched with, I now have a better implementation put together, here's the updated file:
    Code:
    #undef __cplusplus
    #include <stddef.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include <errno.h>
    #include <limits.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdio.h>
    typedef bool(*printProc_t)(int pid, char const *name);
    #ifdef _WIN32
    // omitted
    #else
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/file.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <sys/ptrace.h>
    #define PP___STRING( VALUE ) #VALUE
    #define PP__STRING( VALUE ) PP___STRING( VALUE )
    #define PP_STRING( VALUE ) PP__STRING( VALUE )
    typedef struct POSIX_PROC_
    {
        int pid;
        int imem;
        int omem;
        char path[6 + ( CHAR_BIT * 2 )];
    } POSIX_PROC;
    int posixFindProc( char const *name )
    {
        // Check we can find process
        DIR *dir = opendir( "/proc" );
        if( !dir ) return 0;
        // Gonna need this in a min
        struct dirent *ent = NULL;
        int pid = 0;
        size_t len = 0;
        char path[PATH_MAX] = "/proc/";
        char line[LINE_MAX] = "";
        FILE *file = NULL;
        while( ( ent = readdir( dir ) ) )
        {
            if( !isdigit( *ent->d_name ) ) continue;
            path[6] = '\0';
            strcat( path, ent->d_name );
            strcat( path, "/cmdline" );
            file = fopen( path, "r" );
            if( !file ) continue;
            len = fread( line, 1, LINE_MAX, file );
            if( line[ len ] == '\n' )
                line[ len ] = '\0';
            fclose( file );
            if( strcasecmp( line, name ) == 0 )
            {
                sscanf( ent->d_name, "%d", &pid );
                clodedir(dir);
                return pid;
            }
        }
        closedir(dir);
        return 0;
    }
    bool posixEnumProc( printProc_t printProcCB )
    {
        if ( !printProcCB ) return 0;
        // Check we can find process
        DIR *dir = opendir( "/proc" );
        if( !dir ) return 0;
        // Gonna need this in a min
        struct dirent *ent = NULL;
        int pid = 0;
        size_t len = 0;
        char path[PATH_MAX] = "/proc/";
        char line[LINE_MAX + 1] = "";
        FILE *file = NULL;
        while( ( ent = readdir( dir ) ) )
        {
            if( !isdigit( *ent->d_name ) ) continue;
            path[6] = '\0';
            strcat( path, ent->d_name );
            strcat( path, "/cmdline" );
            file = fopen( path, "r" );
            if( !file ) continue;
            len = fread( line, 1, LINE_MAX, file );
            if( line[ len ] != '\n' ) ++len;
            line[ len ] = '\0';
            fclose( file );
            strtok( line, " \n" );
            sscanf( ent->d_name, "%d", &pid );
            if ( *line != '/' && line[1] && !printProcCB( pid, line ) ) break;
        }
        closedir(dir);
        return 1;
    }
    POSIX_PROC posixOpenProc( int pid, bool makeChild, int flags )
    {
        POSIX_PROC proc = {0};
        proc.pid = pid;
        if( pid <= 0 ) return proc;
        char mem[6 + ( CHAR_BIT * 2 ) + 3 ] = {0};
        sprintf( proc.path, "/proc/%d/", pid );
        strcpy( mem, proc.path );
        strcat( mem, "mem" );
        proc.imem = open( mem, O_RDONLY );
        proc.omem = open( mem, O_WRONLY );
        return proc;
    }
    uintptr_t upseek( int fd, uintptr_t addr )
    {
        if( lseek( fd, 0, SEEK_SET ) <= 0 ) return 0;
        while( addr > PTRDIFF_MAX )
            {
                if( lseek( fd, PTRDIFF_MAX, SEEK_CUR ) <= 0 )
                    return 0;
                addr -= PTRDIFF_MAX;
            }
        return ( lseek( fd, addr, SEEK_CUR ) > 0 ) ? 1 : 0;
    }
    size_t posixRdProc( POSIX_PROC proc, uintptr_t addr, void *buff, size_t size )
    {
        if( proc.imem <= 0 || !upseek( proc.imem, addr ) ) return 0;
        return read( proc.imem, buff, size );
    }
    size_t posixWrProc( POSIX_PROC proc, uintptr_t addr, void *buff, size_t size )
    {
        if( proc.omem <= 0 )
            {
                long result = ptrace( PTRACE_POKEDATA, proc.pid, addr, buff );
                if( result == 0 )
                    ptrace( PTRACE_POKETEXT, proc.pid, addr, buff );
                if( result == 0 )
                    ptrace( PTRACE_POKEUSER, proc.pid, addr, buff );
                return result;
            }
        if( !upseek( proc.omem, addr ) ) return 0;
        return write( proc.omem, buff, size );
    }
    POSIX_PROC posixShutProc( POSIX_PROC proc )
    {
        if( proc.imem > 0 ) close( proc.imem );
        if( proc.omem > 0 ) close( proc.omem );
        return ( POSIX_PROC ) {0};
    }
    bool   posixKillProc( int fdProc )
    {
        return 0;
    }
    #define rawFindProc posixFindProc
    #define rawExecProc posixExecProc
    #define rawEnumProc posixEnumProc
    #define rawOpenProc posixOpenProc
    #define rawRdProc   posixRdProc
    #define rawWrProc   posixWrProc
    #define rawShutProc posixShutProc
    #define rawKillProc posixKillProc
    #define HPROC POSIX_PROC
    #define RAW_IS_PROC_VALID( M_HPROC ) (M_HPROC.pid > 0)
    #define RAW_PROC_O_THREAD 0
    #define RAW_PROC_O_QUERY  0
    #define RAW_PROC_O_EXEC   0
    #define RAW_PROC_O_KILL   0
    #define RAW_PROC_O_MEM_OP 0
    #define RAW_PROC_O_READ   0
    #define RAW_PROC_O_RDWR   0
    #define RAW_PROC_O_WRITE  0
    #define RAW_PROC_O_PAUSE  0
    #define RAW_PROC_O_DEBUG  0
    #endif
    bool printProc( int pid, char const *name )
    {
        fprintf(stdout,"%08X: %s\n", pid, name );
        //puts( name );
        return 1;
    }
    int main( void )
    {
        if ( !rawEnumProc(printProc) )
            return -1;
        /*/
        HPROC proc = rawOpenProc( rawFindProc( "codeblocks" ), 0, RAW_PROC_O_READ );
        if( RAW_IS_PROC_VALID( proc ) )
        {
            proc = rawShutProc( proc );
            return 0;
        }
        //*/
        fgetc( stdin );
        return 0;
    }

  9. #9
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,735
    as for the CHAR_BIT, here's my working based on some variants of the CHAR_BIT definition and an integer of 4 bytes/char
    6 bits = 8 388 607 (7 characters, CHAR_BIT * 2 covers this),
    8 bits = 2 147 483 647 (10 characters, CHAR_BIT * 2 covers this),
    11 bits = 8 796 093 022 207 (13 characters, CHAR_BIT * 2 covers this)
    In the case of 8 bytes I could just multiply by 3
    6bits = 48bits = 16characters including -, 6 * 3 = 18
    8bits = 64bits = 20characters including -, 8 * 3 = 24
    Need I go on?

  10. #10
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,735
    However I will make a define like you suggested

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by awsdert
    as for the CHAR_BIT, here's my working based on some variants of the CHAR_BIT definition and an integer of 4 bytes/char
    6 bits = 8 388 607 (7 characters, CHAR_BIT * 2 covers this),
    8 bits = 2 147 483 647 (10 characters, CHAR_BIT * 2 covers this),
    11 bits = 8 796 093 022 207 (13 characters, CHAR_BIT * 2 covers this)
    In the case of 8 bytes I could just multiply by 3
    6bits = 48bits = 16characters including -, 6 * 3 = 18
    8bits = 64bits = 20characters including -, 8 * 3 = 24
    Need I go on?
    No need to go on: you have demonstrated why your idea of involving CHAR_BIT is a bad idea merely by having to demonstrate working with hypothetical values of CHAR_BIT. Take for example the case of a 10 character decimal string: you could just write a #define with a comment that the length is for a decimal string of at most 10 characters.

    Furthermore, the fact that you have to write "In the case of 8 bytes I could just multiply by 3" means that the CHAR_BIT macro did not help at all. You could have written 24 and it would have made no difference. Using a predefined macro in such cases is helpful when it means that your code becomes portable without you having to make such manual adjustments.

    Note that CHAR_BIT is guaranteed to be at least 8, so your hypothetical scenario of CHAR_BIT being 6 won't happen for a standard conforming implementation in the first place.

    Quote Originally Posted by awsdert
    the shortening of the string was down to how the linux man pages explained the contents of the exe file (enclosed in a bracket variant, can't remember which)
    It is for such cases that comments were invented.
    Last edited by laserlight; 03-25-2015 at 03:51 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,735
    Quote Originally Posted by laserlight View Post
    No need to go on: you have demonstrated why your idea of involving CHAR_BIT is a bad idea merely by having to demonstrate working with hypothetical values of CHAR_BIT. Take for example the case of a 10 character decimal string: you could just write a #define with a comment that the length is for a decimal string of at most 10 characters.

    Furthermore, the fact that you have to write "In the case of 8 bytes I could just multiply by 3" means that the CHAR_BIT macro did not help at all. You could have written 24 and it would have made no difference. Using a predefined macro in such cases is helpful when it means that your code becomes portable without you having to make such manual adjustments.

    Note that CHAR_BIT is guaranteed to be at least 8, so your hypothetical scenario of CHAR_BIT being 6 won't happen for a standard conforming implementation in the first place.
    The point is this code is supposed to be flexible about the target platform which is why I chose to abuse CHAR_BIT here, however I did work out a way to minimise the size issues:
    Code:
    #define INT_DEC_DIG (CHAR_BIT * ((sizeof(int) / CHAR_BIT) + 2))

  13. #13
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by awsdert
    The point is this code is supposed to be flexible about the target platform which is why I chose to abuse CHAR_BIT here
    Well, it is your code, so if you want to abuse CHAR_BIT or find that that is the best compromise for you, then go ahead. However, you should then document this carefully, e.g., write a comment with a rough sketch of the working that your provided, which in itself will show what assumptions you are making.

    Quote Originally Posted by awsdert
    however I did work out a way to minimise the size issues:
    Code:
    #define INT_DEC_DIG (CHAR_BIT * ((sizeof(int) / CHAR_BIT) + 2))
    Note that there are edge cases in which this could be problematic, e.g., suppose CHAR_BIT is 9, sizeof(int) is 8 and INT_MAX is 9223372036854775807. INT_DEC_DIG would then be 18, but the decimal string length of INT_MAX would be 19. Of course, CHAR_BIT is most likely to be 8 these days, so you are unlikely to encounter this problem.

    That said, I do not really have a good compile-time alternative, other than something long and ugly like:
    Code:
    #include <limits.h>
    
    #if    INT_MAX > 9223372036854775807LL
        /* likely decimal string length of 128-bit signed integer */
        #define INT_DEC_DIG 39
    #elif INT_MAX >= 1000000000000000000LL
        #define INT_DEC_DIG 19
    #elif INT_MAX >= 100000000000000000LL
        #define INT_DEC_DIG 18
    #elif INT_MAX >= 10000000000000000LL
        #define INT_DEC_DIG 17
    #elif INT_MAX >= 1000000000000000LL
        #define INT_DEC_DIG 16
    #elif INT_MAX >= 100000000000000LL
        #define INT_DEC_DIG 15
    #elif INT_MAX >= 10000000000000LL
        #define INT_DEC_DIG 14
    #elif INT_MAX >= 1000000000000LL
        #define INT_DEC_DIG 13
    #elif INT_MAX >= 100000000000LL
        #define INT_DEC_DIG 12
    #elif INT_MAX >= 10000000000LL
        #define INT_DEC_DIG 11
    #elif INT_MAX >= 1000000000L
        #define INT_DEC_DIG 10
    #elif INT_MAX >= 100000000L
        #define INT_DEC_DIG 9
    #elif INT_MAX >= 10000000L
        #define INT_DEC_DIG 8
    #elif INT_MAX >= 1000000L
        #define INT_DEC_DIG 7
    #elif INT_MAX >= 100000L
        #define INT_DEC_DIG 6
    #else
        #define INT_DEC_DIG 5
    #endif
    But even this could run into problems for older compilers that do not support long long.

    EDIT:
    Oh, and I neglected to add 1 for the negative sign.
    Last edited by laserlight; 03-25-2015 at 05:53 AM. Reason: Oops, wrong else
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  14. #14
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by laserlight View Post
    I do not really have a good compile-time alternative
    We know that you need (log 2)/(log 10) < 87/289 decimal digits per bit. So, I prefer to use
    Code:
    #include <limits.h>
    #define DECIMAL_SIZE(type) (3 + (87 * sizeof (type) * CHAR_BIT) / 289)
    which is a compile-time constant. It is a slight overestimate, but works well for me. If you want more precision, just use a more precise overestimate of (log 2)/(log 10) like 789/2621.

    The macro defines the size needed for a character buffer to contain the decimal number as a string, so that's why I add three: '-', '0', plus end-of-string NUL byte at minimum. Again, it is oversized by a few chars, but not by too many.

  15. #15
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,735
    suppose CHAR_BIT is 9, sizeof(int) is 8 and INT_MAX is 9223372036854775807. INT_DEC_DIG would then be 18, but the decimal string length of INT_MAX would be 19
    I appreciate your comments, I will make an adjustment for that edge case of yours (thinking maybe a +3 at the end might be sufficient, allows for '\0' character as well), I'll also add that comment like you said (probably help me later anyway), aside from that is there anything else you think I should add/modify?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. how to get process info ( to extract process thread id )
    By umen242 in forum C++ Programming
    Replies: 4
    Last Post: 02-12-2009, 01:08 PM
  2. How to Use POSIX for IPC Message To Another Process?
    By dedham_ma_man in forum C Programming
    Replies: 4
    Last Post: 09-28-2007, 03:06 PM
  3. POSIX Signal Handling
    By nine-hundred in forum C Programming
    Replies: 8
    Last Post: 04-13-2007, 02:08 PM
  4. Process sending file descriptors to another process
    By Yasir_Malik in forum C Programming
    Replies: 4
    Last Post: 04-07-2005, 07:36 PM
  5. Child Process & Parent Process Data :: Win32
    By kuphryn in forum Windows Programming
    Replies: 5
    Last Post: 09-11-2002, 12:19 PM