objective-c get list of processes, their paths AND arguments - objective-c

Getting the list of processes and their path is quite easy;
int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
pid_t pids[1024];
bzero(pids, 1024);
proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));
for (int i = 0; i < numberOfProcesses; ++i) {
if (pids[i] == 0) { continue; }
char pathBuffer[PROC_PIDPATHINFO_MAXSIZE];
bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE);
proc_pidpath(pids[i], pathBuffer, sizeof(pathBuffer));
char arguments[KERN_PROCARGS2];
if (strlen(pathBuffer) > 0) {
printf("path: %s\n", pathBuffer);
}
}
However, I would also like to get any arguments that were used to launch these processes. I can't seem to find how to do this. Any pointers?

A pointer? The ps command lists them and its source is available as part of Apple's open source: ps folder.

./build64.sh # Build cmdline app for 64-bit Intel Mac
# Enumerate all processes running and print the argvs
./xproc --pid-enum | xargs -L1 ./xproc --cmd-from-pid
Calling the functions directly will be faster than running a new task.
Source code can be built for Windows, MacOS, Linux, and FreeBSD.
Feel free to borrow any portion of code you may need from it:
https://github.com/time-killer-games/xproc

Related

Unable to open file after FUSE mount

I'm trying out FUSE for the first time by following the official GitHub Repo's example.
I have done the following:
created a mount directory called mount_dir that contains file hello.txt.
update /etc/fuse.conf with user_allow_other as mentioned in various forums and posts
Added bunch of printf() statements in hello_ll.c at all function entry points.
Executed ./hello_ll -o allow_other -f /home/hemalkumar/mount_dir
Executed ./test_stat. It calculate the number of pages in the file. Just some business logic, nothing fancy!
test_stat.c
#define PAGE_SIZE 4096
int main() {
char* filename = "/home/hemalkumar/mount_dir/hello.txt";
int fd = open(filename, O_RDONLY);
if (fd == -1) {
printf("INVALID file:%s\n", filename);
close(fd);
return -1;
}
struct stat sb;
if (fstat(fd, &sb) == -1) {
close(fd);
return -1;
}
int pages = sb.st_size/PAGE_SIZE + (sb.st_size % PAGE_SIZE != 0);
printf("pages:%d\n", pages);
return 0;
}
Issue:
When I execute test_stat without mounting FUSE, it works fine. However, running it after step#4 shows an error INVALID file:/home/hemalkumar/mount_dir/hello.txt.
I have updated /etc/fuse.conf file to allow other user, and passing flags during startup of hello_ll. Don't know what permission issues it is having.
Any pointers will be appreciated!
Thanks!

Detect if a Tcl script is run in a background process

I'm looking for a preferably cross-platform way to detect from within a Tcl script if the interpreter is running in a foreground or in a background process.
I've seen how to do it via ps (or /proc/$$/stat on Linux); is there a better way or do I have to hack something around that approach? I already have a utility library written in C so exposing the lowlevel API that ps also uses so I don't have to parse process output (or special file content) would be fine.
There's no truly cross-platform notion of foreground, but the main platforms do have ways of doing it according to the notion they have of foreground.
Linux, macOS, and other Unix:
For determining if a process is foreground or not, you need to check if its process group ID is the terminal's controlling process group ID. For Tcl, you'd be looking to surface the getpgrp() and tcgetpgrp() system calls (both POSIX). Tcl has no built-in exposure of either, so you're talking either a compiled extension (may I recommend Critcl for this?) or calling an external program like ps. Fortunately, if you use the latter (a reasonable option if this is just an occasional operation) you can typically condition the output so that you get just the information you want and need to do next to no parsing.
# Tested on macOS, but may work on other platforms
proc isForeground {{pid 0}} {
try {
lassign [exec ps -p [expr {$pid ? $pid : [pid]}] -o "pgid=,tpgid="] pgid tpgid
} on error {} {
return -code error "no such process"
}
# If tpgid is zero, the process is a daemon of some kind
expr {$pgid == $tpgid && $tpgid != 0}
}
Windows
There's code to do it, and the required calls are supported by the TWAPI extension so you don't need to make your own. (WARNING! I've not tested this!)
package require twapi_ui
proc isForeground {{pid 0}} {
set forground_pid [get_window_thread [get_foreground_window]]
return [expr {($pid ? $pid : [pid]) == $foreground_pid}]
}
Thanks to Donal I came up with the implementation below that should work on all POSIX Unix variants:
/*
processIsForeground
synopsis: processIsForeground
Returns true if the process is running in the foreground or false
if in the background.
*/
int IsProcessForegroundCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
/* Check the arg count */
if (objc != 1) {
Tcl_WrongNumArgs(interp, 1, objv, NULL);
return TCL_ERROR;
}
int fd;
errno = 0;
if ((fd = open("/dev/tty", O_RDONLY)) != -1) {
const pid_t pgrp = getpgrp();
const pid_t tcpgrp = tcgetpgrp(fd);
if (pgrp != -1 && tcpgrp != -1) {
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(pgrp == tcpgrp));
close(fd);
return TCL_OK;
}
close(fd);
}
Tcl_SetErrno(errno);
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "processIsForeground: ", (char *)Tcl_PosixError(interp), NULL);
return TCL_ERROR;
}
int Pextlib_Init(Tcl_Interp *interp)
{
if (Tcl_InitStubs(interp, "8.4", 0) == NULL)
return TCL_ERROR;
// SNIP
Tcl_CreateObjCommand(interp, "processIsForeground", IsProcessForegroundCmd, NULL, NULL);
if (Tcl_PkgProvide(interp, "Pextlib", "1.0") != TCL_OK)
return TCL_ERROR;
return TCL_OK;
}

How to get dylib version information which are in a directory

I wanted to get the dylib version. I've a dylib path for which I wanted to get the version number.
I've tried "otool -L" command and it's giving me the proper output but as per the requirements I can't use it, since I've 100 of dylib in a directory for which I wanted to get the version information and I can't run "otool" command for each dylib through NSTask and NSPipe.
I've also found the NSVersionOfLinkTimeLibrary() function to get the dylib version, but as per the documentation NSVersionOfLinkTimeLibrary returns the version number for linked libraries and not for other dylib.
Any help on this would be helpful.
Thanks.
Omkar
I've solved it by writing my own dylib parser. Below is the code snippet
- (int64_t)getDylibVersion :(NSString *)dylibPth
{
const char* strFilePath = [dylibPth UTF8String];
FILE* fileHandle = fopen(strFilePath, "rb");
struct mach_header mh;
if(fileHandle)
{
size_t bytesRead = fread(&mh, 1, sizeof(mh), fileHandle);
if(bytesRead == sizeof(mh))
{
if((mh.magic == MH_MAGIC_64 || mh.magic == MH_MAGIC) && mh.filetype == MH_DYLIB)
{
for(int j = 0; j < mh.ncmds; j++)
{
union
{
struct load_command lc;
struct dylib_command dc;
} load_command;
if (sizeof(load_command.lc) != fread(&load_command.lc, 1, sizeof(load_command.lc), fileHandle))
goto fail;
switch (load_command.lc.cmd)
{
case LC_SEGMENT:
break;
case LC_UUID:
break;
case LC_DYLD_INFO_ONLY:
break;
case LC_SYMTAB:
break;
case LC_LOAD_DYLIB:
break;
case LC_ID_DYLIB:
{
if (sizeof(load_command) - sizeof(load_command.lc) != fread(&load_command.lc + 1, 1, sizeof(load_command) - sizeof(load_command.lc), dylib_handle))
goto fail;
fclose(fileHandle);
return(load_command.dc.dylib.current_version);
}
default:
break;
}
if (0 != fseek(fileHandle, load_command.lc.cmdsize - sizeof(load_command.lc), SEEK_CUR))
goto fail;
}
}
}
}
fail:
fclose(fileHandle);
return (-1);
}
Note that Mach-O dylib version numbers are encoded as 32-bit unsigned integers, with the major version in the high 16 bits, the minor version in bits 8 through 15, and the patch level in the low 8 bits:
uint32_t version = …;
uint32_t major = version >> 16;
uint32_t minor = (version >> 8) & 0xff;
uint32_t revision = version & 0xff;
Note also that the above code will only work for "thin" binaries. "Fat," multi-architecture binaries start with a fat header, which you'll need to negotiate first to find the slice for your desired architecture. Moreover, the above only works with architectures of the running architecture's endianness.
The way I see it, you have 2 options.
Load each dylib into your process and lookup the Mach-O headers on each, looking for the version numbers. The documentation should be complete and thorough enough to get you started.
Open each dylib as a normal file, and read and parse the Mach-O headers yourself. This avoid having to load each dylib into the process, but it does mean you need to then either parse the Mach-O binary format yourself, or find a library that can do it for you (I don't know of any off the top of my head).

What is causing this command line error?

I am trying to programatically call the "top" command. The following is the code used:
char buffer [128];
char* threadsPointer;
char* procPointer;
NSString* numberOfThreadsString;
NSString* numberOfProcString;
FILE* output = popen("/usr/bin/top", "r");
while (fgets(buffer, sizeof(buffer), output) != NULL)
{
if ((procPointer = strstr(buffer, "Processes:")) != NULL)
{
procPointer += strlen("Proceses: ");
strcpy(buffer, procPointer);
numberOfProcString = [NSString stringWithUTF8String: buffer];
}
if ((threadsPointer = strstr(buffer, "sleeping,")) != NULL)
{
threadsPointer += strlen("sleeping, ");
strcpy(buffer, threadsPointer);
numberOfThreadsString = [NSString stringWithUTF8String: buffer];
}
}
NSLog(#"Proc: %#\nThreads: %#\n\n\n", numberOfProcString, numberOfThreadsString);
Instead of giving valid output, I keep getting the error: "Error opening terminal: unknown". I commended out the whole piece of code to identify the problem, and realized that its the line: FILE* output = popen ("/usr/bin/top", "r"); that is causing the error.
Does anyone have an idea of what I am doing wrong? Note I am on Mountain Lion OS X building an app for OSX not iOS.
top on MacOSX requires that its standard output or standard error be connected to a valid terminal to run. When you invoke it without a terminal (or a $TERM environment variable set to a valid terminal name, like "vt100"), it gives you that error: Error opening terminal: unknown.
You really shouldn't be using top for this, since it's an interactive program that requires a terminal. You should just be using ps.
you can try "top -l 1".
"-l 1" mean 1 sample, I think it should run top in non-interactive mode, print the result and exit.
On linux the command is "top -n 1" ("-n 1" mean run only 1 iteration, which should be equivalent to "top -l 1" on mac osx).
If you want specific information or all the details you should use "/proc" filesystem.
You can readdir() and fopen() all the files in /proc which contains currently running processes and get a lot of information, like for example what files are open by some process, or what ports is the process listening on.
'top' command opens a terminal and waits for user input. You won't be able to use it in a an automated script

How do I get a list of the window titles on the Mac OSX?

I want to get the list of window titles of the currently running applications.
On windows I have EnumWndProc and GetWindowText.
On Linux I have XGetWindowProperty and XFetchName.
What is the Native Mac equivalent?
A few potentially useful references:
NSWindowList()
NSWorkspace -launchedApplications and +runningApplications
CGWindowListCreate() and CGWindowListCopyWindowInfo() (requires 10.5)
CGSGetWindowProperty()
CGSGetWindowProperty is not officially documented, but I believe you can use it with the an item of NSWindowList() as follows (completely untested):
OSErr err;
CGSValue titleValue;
char *title;
CGSConnection connection = _CGSDefaultConnection();
int windowCount, *windows, i;
NSCountWindows(&windowCount);
windows = malloc(windowCount * sizeof(*windows));
if (windows) {
NSWindowList(windowCount, windows);
for (i=0; i < windowCount; ++i) {
err = CGSGetWindowProperty(connection, windows[i],
CGSCreateCStringNoCopy("kCGSWindowTitle"),
&titleValue);
title = CGSCStringValue(titleValue);
}
free(windows);
}
In AppleScript, it's really easy:
tell application "System Events" to get the title of every window of every process
You can call applescript from within an application using NSAppleScript or use appscript as an ObjC-AppleScript bridge. With Leopard, you can use the Scripting Bridge (more untested code):
SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:#"com.apple.systemevents"];
SBElementArray *processes = [systemEvents processes];
for (SystemEventsProcess* process in processes) {
NSArray *titles = [[process windows] arrayByApplyingSelector:#selector(title)];
}
You could even try it in one long call, if you don't care about readability.
SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:#"com.apple.systemevents"];
NSArray *titles = [[[systemEvents processes]
arrayByApplyingSelector:#selector(windows)]
arrayByApplyingSelector:#selector(arrayByApplyingSelector:)
withObject:#selector(title)];
The compiler will complain that #selector(title) is the wrong type, but it should work. Hand roll some delegation and you could turn the call into [[[systemEvents processes] windows] title].
The CGSPrivate.h header that's floating around isn't directly compatible with OS X 10.8 in that CGSGetWindowProperty() no longer exists (well, it does, but you can't link to it anymore). So add these two lines to the CGSPrivate.h file -- I went ahead and figured this out myself after many hours searching Google -- to get it to work:
extern CGSConnection CGSDefaultConnectionForThread(void);
extern CGError CGSCopyWindowProperty(const CGSConnection cid, NSInteger wid, CFStringRef key, CFStringRef *output);
Adapting outis's code, here's a way of iterating through each window title. I have tested this with clang 4.2 on Mountain Lion:
CFStringRef titleValue;
CGSConnection connection = CGSDefaultConnectionForThread();
NSInteger windowCount, *windows;
NSCountWindows(&windowCount);
windows = (NSInteger*) malloc(windowCount * sizeof(NSInteger));
if (windows) {
NSWindowList(windowCount, windows);
for (int i = 0; i < windowCount; ++i)
{
CGSCopyWindowProperty(connection, windows[i], CFSTR("kCGSWindowTitle"), &titleValue);
if(!titleValue) //Not every window has a title
continue;
//Do something with titleValue here
}
free(windows);
}
Some other stuff I found out includes the following:
No window title exceeds 127 bytes.
Window titles are encoded with kCFStringEncodingMacRoman
So, if you want it as a C-string, write something like this:
char *cTitle[127] = {0};
CFStringGetCString(titleValue,cTitle,127,kCFStringEncodingMacRoman);
Personally, I'd recommend doing it this way since the Accessibility API is a total pain and requires extra permissions.
Hope this helps someone! Cheers!