@ -1,14 +1,32 @@
# include <unistd.h>
# include <getopt.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <stdbool.h>
# include <mach/machine.h>
# include <sys/types.h>
# include <sys/sysctl.h>
# include <libgen.h>
# define VERSION "0.4.0"
# define noErr 0
# define VERSION "0.2.0"
# define DEFAULT_BUFFER_SIZE 4096
# define SYSCTL_ERROR 1
# define is_match(x) (x == 0)
enum fmt { JSON = 0 , COLUMNS } ;
enum show { ALL = 0 , ARM64 , X86_64 } ;
static int max_arg_size = 0 ;
typedef struct ProcInfo {
bool ok ;
char * name ;
char arch [ 10 ] ;
} procinfo ;
// arch_info() is due to the spelunking work of Patrick Wardle <https://www.patreon.com/posts/45121749>
static char * arch_info ( pid_t pid ) {
@ -42,7 +60,7 @@ static char *arch_info(pid_t pid) {
if ( noErr ! = sysctl ( mib , ( u_int ) length , & procInfo , & size , NULL , 0 ) ) return ( " arm64 " ) ; //get proc info
//'P_TRANSLATED' set? set architecture to 'Intel '
//'P_TRANSLATED' set? set architecture to 'x86_64 '
return ( ( P_TRANSLATED = = ( P_TRANSLATED & procInfo . kp_proc . p_flag ) ) ? " x86_64 " : " arm64 " ) ;
}
@ -51,61 +69,104 @@ static char *arch_info(pid_t pid) {
}
// The below is modified code from: <https://gist.github.com/s4y/1173880/9ea0ed9b8a55c23f10ecb67ce288e09f08d9d1e5>
// and is Copyright (c) 2020 DeepTech, Inc. (MIT License).
//
// The code below the standard way to do this and I originally only copied it due to some Objective-C components.
// While those components are no longer in use, the ACK stays b/c I'm thankful I didn't have to write Objective-C
// for the initial version :-)
// retrieve process info
procinfo proc_info ( pid_t pid ) {
size_t size = max_arg_size ;
procinfo p ;
int mib [ 3 ] = { CTL_KERN , KERN_PROCARGS2 , pid } ;
struct kinfo_proc * info ;
size_t length ;
int count ;
// get size for buffer
( void ) sysctl ( mib , 3 , NULL , & length , NULL , 0 ) ;
int enumerate_processes ( enum fmt output_type ) {
char * buffer = ( char * ) calloc ( length + 32 , sizeof ( char ) ) ; // need +32 b/c this is busted on Big Sur
static int maxArgumentSize = 0 ;
// get the info
if ( sysctl ( mib , 3 , buffer , & size , NULL , 0 ) = = 0 ) {
if ( maxArgumentSize = = 0 ) {
size_t size = sizeof ( maxArgumentSize ) ;
if ( sysctl ( ( int [ ] ) { CTL_KERN , KERN_ARGMAX } , 2 , & maxArgumentSize , & size , NULL , 0 ) = = - 1 ) {
perror ( " sysctl argument size " ) ;
maxArgumentSize = 4096 ; // Default
}
// copy the info
p . ok = TRUE ;
p . name = buffer ;
strncpy ( p . arch , arch_info ( pid ) , 10 ) ;
} else {
free ( buffer ) ;
p . ok = FALSE ;
}
return ( p ) ;
}
// output one line of process info with architecture info
void output_one ( enum fmt output_type , pid_t pid , procinfo p , bool only_basename ) {
if ( output_type = = COLUMNS ) {
printf (
" %7d %6s %s \n " ,
pid ,
p . arch ,
( only_basename ? basename ( ( char * ) ( p . name + sizeof ( int ) ) ) : p . name + sizeof ( int ) )
) ;
} else if ( output_type = = JSON ) {
printf (
" { \" pid \" :%d, \" arch \" : \" %s \" , \" name \" : \" %s \" } \n " ,
pid ,
p . arch ,
( only_basename ? basename ( ( char * ) ( p . name + sizeof ( int ) ) ) : p . name + sizeof ( int ) )
) ;
}
}
// walk through process list, get PID and name, then pass that on to output_one() to
// grab the architecture and do the actual output
int enumerate_processes ( enum fmt output_type , enum show to_show , bool only_basename ) {
int mib [ 3 ] = { CTL_KERN , KERN_PROC , KERN_PROC_ALL } ;
struct kinfo_proc * info ;
size_t length ;
int count ;
if ( sysctl ( mib , 3 , NULL , & length , NULL , 0 ) < 0 ) return ( 1 ) ;
// get the running process list
if ( sysctl ( mib , 3 , NULL , & length , NULL , 0 ) < 0 ) return ( SYSCTL_ERROR ) ;
if ( ! ( info = malloc ( length ) ) ) return ( 1 ) ;
// make some room for results
if ( ! ( info = calloc ( length , sizeof ( char ) ) ) ) return ( SYSCTL_ERROR ) ;
// get the results
if ( sysctl ( mib , 3 , info , & length , NULL , 0 ) < 0 ) {
free ( info ) ;
return ( 1 ) ;
return ( SYSCTL_ERROR ) ;
}
// how many results?
count = ( int ) ( length / sizeof ( struct kinfo_proc ) ) ;
// for each result
for ( int i = 0 ; i < count ; i + + ) {
pid_t pid = info [ i ] . kp_proc . p_pid ;
pid_t pid = info [ i ] . kp_proc . p_pid ; // get PID
if ( pid = = 0 ) continue ;
size_t size = maxArgumentSize ;
procinfo p = proc_info ( pid ) ; // get process info
char * buffer = ( char * ) malloc ( length ) ;
if ( sysctl ( ( int [ ] ) { CTL_KERN , KERN_PROCARGS2 , pid } , 3 , buffer , & size , NULL , 0 ) = = 0 ) {
if ( output_type = = COLUMNS ) {
printf ( " %7d %6s %s \n " , pid , arch_info ( pid ) , buffer + sizeof ( int ) ) ;
} else if ( output_type = = JSON ) {
printf ( " { \" pid \" :%d, \" arch \" : \" %s \" , \" name \" : \" %s \" } \n " , pid , arch_info ( pid ) , buffer + sizeof ( int ) ) ;
if ( p . ok ) {
if (
( to_show = = ALL ) | |
( ( to_show = = ARM64 ) & & is_match ( strncmp ( " arm " , p . arch , 3 ) ) ) | |
( ( to_show = = X86_64 ) & & is_match ( strncmp ( " x86 " , p . arch , 3 ) ) )
) {
output_one ( output_type , pid , p , only_basename ) ;
}
free ( p . name ) ;
}
free ( buffer ) ;
}
free ( info ) ;
@ -114,36 +175,110 @@ int enumerate_processes(enum fmt output_type) {
}
// call to display cmdline tool help
void help ( ) {
printf ( " archinfo %s \n " , VERSION ) ;
printf ( " boB Rudis <bob@rud.is> \n " ) ;
printf ( " \n " ) ;
printf ( " archinfo outputs a list of running processes with the architecture (x86_64/amd64) \n " ) ;
printf ( " archinfo outputs a list of running processes with architecture (x86_64/amd64) information \n " ) ;
printf ( " \n " ) ;
printf ( " USAGE: \n " ) ;
printf ( " archinfo [FLAG ] \n " ) ;
printf ( " archinfo [--arm|--x86] [--basename] [--columns|--json] [--pid # ] \n " ) ;
printf ( " \n " ) ;
printf ( " FLAG: \n " ) ;
printf ( " FLAGS/OPTIONS: \n " ) ;
printf ( " --arm Only show arm64 processes (default is to show both) \n " ) ;
printf ( " --x86 Only show x86_64 processes (default is to show both) \n " ) ;
printf ( " --basename Only show basename of process executable \n " ) ;
printf ( " --columns Output process list in columns (default) \n " ) ;
printf ( " --json Output process list in ndjson \n " ) ;
printf ( " --columns Output process list in columns \n " ) ;
printf ( " --pid # Output process architecture info for the specified proces s \n " ) ;
printf ( " --help Display this help text \n " ) ;
}
void init ( ) {
if ( max_arg_size = = 0 ) {
size_t size = sizeof ( max_arg_size ) ;
if ( sysctl ( ( int [ ] ) { CTL_KERN , KERN_ARGMAX } , 2 , & max_arg_size , & size , NULL , 0 ) = = - 1 ) {
perror ( " sysctl argument size " ) ;
max_arg_size = DEFAULT_BUFFER_SIZE ;
}
}
}
int main ( int argc , char * * argv ) {
if ( argc > = 2 ) {
if ( strcmp ( " --json " , argv [ 1 ] ) = = 0 ) {
return ( enumerate_processes ( JSON ) ) ;
} else if ( strcmp ( " --columns " , argv [ 1 ] ) = = 0 ) {
return ( enumerate_processes ( COLUMNS ) ) ;
} else if ( strcmp ( " --help " , argv [ 1 ] ) = = 0 ) {
help ( ) ;
return ( 0 ) ;
int c ;
enum show to_show = ALL ;
bool show_help = FALSE ;
bool do_one = FALSE ;
bool only_basename = FALSE ;
pid_t pid = - 1 ;
enum fmt output_type = COLUMNS ;
while ( true ) {
int this_option_optind = optind ? optind : 1 ;
int option_index = 0 ;
static struct option long_options [ ] = {
{ " arm " , no_argument , 0 , ' a ' } ,
{ " x86 " , no_argument , 0 , ' x ' } ,
{ " basename " , no_argument , 0 , ' b ' } ,
{ " json " , no_argument , 0 , ' j ' } ,
{ " columns " , no_argument , 0 , ' c ' } ,
{ " help " , no_argument , 0 , ' h ' } ,
{ " pid " , required_argument , 0 , ' p ' } ,
{ 0 , 0 , 0 , 0 }
} ;
c = getopt_long ( argc , argv , " axbjchp: " , long_options , & option_index ) ;
if ( c = = - 1 ) break ;
switch ( c ) {
case ' a ' : to_show = ARM64 ; break ;
case ' x ' : to_show = X86_64 ; break ;
case ' b ' : only_basename = TRUE ; break ;
case ' p ' : do_one = TRUE ; pid = atoi ( optarg ) ; break ;
case ' h ' : show_help = TRUE ; break ;
case ' j ' : output_type = JSON ; break ;
case ' c ' : output_type = COLUMNS ; break ;
}
} else {
return ( enumerate_processes ( COLUMNS ) ) ;
}
init ( ) ;
// only show help if --help is in the arg list
if ( show_help ) {
help ( ) ;
return ( 0 ) ;
}
// only do one process if --pid is in the arg list
if ( do_one ) {
if ( pid > 0 ) {
procinfo p = proc_info ( pid ) ;
if ( p . ok ) {
output_one ( output_type , pid , p , only_basename ) ;
free ( p . name ) ;
return ( 0 ) ;
}
}
return ( SYSCTL_ERROR ) ;
}
// otherwise do them all
return ( enumerate_processes ( output_type , to_show , only_basename ) ) ;
}