Skip to content

[WIP] [Process] Add functions to get current process PID and name #12666

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

MrOnlineCoder
Copy link
Contributor

My attempt to resolve #12615

Adds two functions as requested by issue author:

int SDL_GetCurrentProcessId(void);

and

const char* SDL_GetCurrentProcessName(void);

I have to note that this is very rough draft and I would appreciate assistance in regards to API design and cross-platform code, as I am new to this.

Therefore, some key points I would like to mention:

  1. I have considered implementing the suggested alternative:

GetCurrentProcess returning a SDL_Process structure with the properties being the current pid and a new property being the process name (SDL.process.name)

I decided to not go this way because I didn't like the idea of manually/artificially building the SDL_Process structure to represent currently running process - it would require to add all current (and future) properties to SDL_Process object, including stdio streams, add safeguards to all other process functions such as kill/wait/destroy, needing to then introduce a way to check if the provided process is the current one. Moreover, while building the internal SDL_ProcessData for SDL_Process member is trivial on POSIX (it requires copying one pid_t field from getpid()) - on Windows it requires a full PROCESS_INFORMATION structure, needing to provide handles and IDs of process and primary thread - I was not sure what to consider "primary thread" in that case, and if it was okay to assume main thread as such.
Therefore I used simpler solution of introducing two new functions. This may be okay unless it's highly desirable to be able to represent current process as fully valid SDL_Process instance, as this would mean any possible new features that will use SDL_Process may be used on the current process too.

  1. I am not sure on what is meant by "process/executable name" in the issue. One of the possible use-case mentions:
  • maybe some games want to verify that users didn't tamper or execute any executables that are not approved by the game (i.e. patched for some cheats and having a separately patched exe so that Steam or other platforms don't overwrite it)

but that would possibly mean that instead of just executable filename, we may be interested in full path including the directory and the executable filename, to check both that the filename was not changed, and that the game is run from Steam directory or similar. In current PR version I have assumed the more straightforward option - executable filename only, so if program is run as /home/test/myapp, SDL_GetCurrentProcessName should return myapp

  1. It seems from both existing SDL source code (for example code for SDL_GetBasePath) and my findings online, there is no single universal way to get the executable name on POSIX platforms - it differs for Linux and MacOS at least. Therefore it uses BSD-specific getprogname() and GNU-specific program_invocation_name to return the executable name. I don't know how to handle other platforms or if it's needed at all.
  2. The previous step could've been implemented by re-using existing enormous ifdef/else block from SDL_GetBasePath implementation as it fetches the executable full path and then truncates it to last path separator (dropping the filename), but I am not sure how to correctly design that - should SDL_GetBasePath code be split into parts, one for fetching the executable path, to be re-used by process API, and the second one to actually take the directory (base path) from it - is this process -> filesystem modules reference acceptable?
  3. It also introduces the SDL_InitProcessManagement and SDL_QuitProcessManagement init and quit callbacks to free the cached process name. I am not sure if caching PID is needed, unless getpid() system call cost is worth that.

Again, I would appreciate any comments and suggestions in right direction.

Comment on lines +519 to +530
char *SDL_SYS_GetCurrentProcessName(void)
{
#if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_FREEBSD)
char *processPath = SDL_strdup(getprogname());
#elif defined(SDL_PLATFORM_LINUX)
char *processPath = SDL_strdup(program_invocation_name);
#else
char *processPath = NULL;
#endif

return processPath;
}
Copy link
Contributor

@Kontrabant Kontrabant Mar 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will return the invocation name as found in argv[0], and is incorrect when symlinks are used. Your idea to split the executable path functionality out of the base path functionality is probably the way to go.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could replace the rather bare-bones SDL_GetExeName() functionality in src/core/unix/SDL_appid.c as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please help me clear the confusion on where to actually implement the function for fetching the executable name, or to be exact, what should be the cross-platform approach to it.

We have quite a few platfrom-dependent implenentations for filesystem module: android, cocoa, haiku, gdk, ps2, psp, unix, windows, vita, etc... While for process we have only division into windows and unix. Is this because process creation is more standardized than filesystem access? Or does it mean process API may not be supported on some systems like ps2 or haiku?

Some of the SDL_GetBasePath implementations do not parse the directory name out of executable path, for example Emscripten just uses "/" as a return value, and PS2 returns getcwd(), which means not every SDL_GetBasePath implementation may contain a valid way to achieve the executable path, that can be used to split into two functions as mentioned above.

How should I proceed?

Copy link

@thedarknomad thedarknomad Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrOnlineCoder

but that would possibly mean that instead of just executable filename, we may be interested in full path including the directory and the executable filename, to check both that the filename was not changed, and that the game is run from Steam directory or similar

No, I only meant the actual executable (as stated by the workaround of using args[0], being path-free). In case you do want such checks for the exe source path, we already have SDL_GetBasePath. So a supposed SDL_GetCurrentProcessName() would return myapp (for POSIX) or myapp.exe (for win-like systems).

I also stated the ideal way is to cache it for further access since it won't change, similar to SDL_GetBasePath.

static char *CachedBasePath = NULL;

const char *SDL_GetBasePath(void)
{
    if (!CachedBasePath) {
        CachedBasePath = SDL_SYS_GetBasePath();
    }
    return CachedBasePath;
}

there is no single universal way to get the executable name on POSIX platforms

Indeed no. You'd need #ifdefs unfortunately. See SDL_GetExeName() as a starting point.

What I found by looking around (but I don't have a *nix to test them on):

@Kontrabant

This will return the invocation name as found in argv[0], and is incorrect when symlinks are used

Well, technically speaking it is still correct. While not talking about games, at work a very modular production system uses configurable modules for each component and it has only dedicated executables on a *nix system but we have over 40 components starting as processes and each are started as a symlink to the what exe is proper for that component. The way we monitor them is to see the "exe" that started it, which is the symlink. If we had resolved the symlink, we wouldn't know what component we're actually monitoring when it initiates (before configs are loaded and so on). While I know I am talking about enterprise systems here, let's consider SDL can be used for other types of apps as well, not just games :) There are built-in ways to treat this and for those using C++, the filesystem namespace is very easy to use, so I wouldn't worry about it. OTOH, the only reason I proposed argv[0] was as a workaround if the system won't allow a method for that due to sandboxing (I am thinking maybe consoles or some mobile devices possibly?)

That could replace the rather bare-bones SDL_GetExeName() functionality in src/core/unix/SDL_appid.c as well.

Indeed, it's good as a starting point since it covers most

SDL_SYS_GetCurrentProcessName

With the mention that the process name should not contain the path; it's not the same as a pseudo-GetCurrentExeFilePath.

Copy link

@thedarknomad thedarknomad Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrOnlineCoder

I would appreciate assistance in regards to API design and cross-platform code

Not sure it's the best way, but good starting code that I imagine would probably be (this is more or less pseudo code, as I am just typing it here, so typos or compilation errors might occur, but might serve as a starting point; consider some is also guess work :) ):

Process id

include/SDL_process.h

extern SDL_DECLSPEC SDL_ProcessID SDLCALL SDL_GetCurrentProcessID(void); // SDL_ProcessID maybe? depends if the type is different on each platform

src/process/SDL_sysprocess.h

SDL_ProcessID SDL_SYS_GetCurrentProcessID(void); // (or hardcoded type? see above note)

src/process/windows/SDL_windowsprocess.c

SDL_ProcessID SDL_SYS_GetCurrentProcessID(void)
{
    return ::GetCurrentProcessId();
}

src/process/posix/SDL_posixprocess.c

SDL_ProcessID SDL_SYS_GetCurrentProcessID(void)
{
    return ::getpid();
}

src/process/SDL_process.c

SDL_ProcessID SDL_GetCurrentProcessID(void)
{
    return SDL_SYS_GetCurrentProcessID();
}

EXE name

include/SDL_filesystem.h

extern SDL_DECLSPEC char * SDLCALL SDL_GetCurrentExe(void); // or SDL_GetCurrentExeFilename?

src/filesystem/SDL_sysfilesystem.h

extern char *SDL_SYS_GetCurrentExe(void);

src/filesystem/windows/SDL_sysfilesystem.c

char *SDL_SYS_GetCurrentExe(void)
{
    // [GetModuleFileNameW](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew)
    // remember to switch to char* unicode; check SDL_SYS_GetCurrentDirectory() for an example with WIN_StringToUTF8W
}

src/filesystem/cocoa/SDL_sysfilesystem.m

Bundled app: executablePath
Non-Bundled app: _NSGetExecutablePath

src/filesystem/dummy/SDL_sysfilesystem.c

char *SDL_SYS_GetCurrentExe(void)
{
    SDL_Unsupported();
    return NULL;
}

src/filesystem/emscripten/SDL_sysfilesystem.c

No clue

src/filesystem/gdk/SDL_sysfilesystem.c

char *SDL_SYS_GetCurrentExe(void)
{
    // [GetModuleFileNameA](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamea)
}

src/filesystem/haiku/SDL_sysfilesystem.c

See this example

src/filesystem/n3ds/SDL_sysfilesystem.c

Most likely no way to get it

src/filesystem/posix/SDL_sysfilesystem.c

char *SDL_SYS_GetCurrentExe(void)
{
    // should work with **/proc/self/exe** in most cases; possibly needs handling if the symlink is missing? not sure if this should happen on linux or other posix-es
}

src/filesystem/riscos/SDL_sysfilesystem.c

As far as I could see from the documentation at first glance, there is no way to track the currently running image reliably

src/filesystem/unix/SDL_sysfilesystem.c

char *SDL_SYS_GetCurrentExe(void)
{
    // see SDL_GetExeName() or maybe replace it as @Kontrabant  suggested
}

src/filesystem/vita/SDL_sysfilesystem.c

Doubtful it can be done on Vita

src/filesystem/SDL_sysfilesystem.c

static char *CachedExeName = NULL;

const char *SDL_GetCurrentExe(void)
{
    if (!CachedExeName ) {
        CachedExeName = SDL_SYS_GetCurrentExe();
    }
    return CachedExeName ;
}

Copy link
Contributor Author

@MrOnlineCoder MrOnlineCoder Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @thedarknomad thanks for such in-depth example, although I was more asking about general idea :D

My concern is that okay for GetCurrentExe to be a part of SDL filesystem api?

Also:

No, I only meant the actual executable (as stated by the workaround of using args[0], being path-free). In case you do want such checks for the exe source path, we already have SDL_GetBasePath. So a supposed SDL_GetCurrentProcessName() would return myapp (for POSIX) or myapp.exe (for win-like systems).

Is it guaranteed on all platforms that "Get the directory where the application was run from." (SDL_GetBasePath wiki) would really equal to the folder where executable is located? There are comments on MacOS and 3DS in the wiki already, but still...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrOnlineCoder
Regarding the examples, I am hoping they help you even if a little. They were based on some quick research (as I don't have those platforms) after reading up on the APIs and some articles. Which is also why I can't assure you on their validity.

I don't have those platforms, so I can't test. I am sure there are SOME edge cases, but I wouldn't focus on them as those might be the exception, not the rule. And to be fair, the general programming law is: "I coded this to be used properly as so, I added some protections or workarounds if you call it wrongly like so so that it still works right, but if you bypass those and the app behaves incorrectly, I am not responsible for your stup... pro-activeness" :)

For the exe name function , I recommend you move it to the filesystem category (see my suggestions) and call it SDL_GetExeName (or SDL_GetExecutableName or SDL_GetCurrentExeFilename or whatever else) since it's more of a file-thing than a process thing. Processes have ids or handles, but my topic addressed the process name just to give a general idea.

Also, make sure you remove the pathname to the exe in the pseudo-getexe function. I think if someone does want to see how it was launched (i.e. via some relative path or from the home directory or anything other, they can capture / track main() or WinMain(). As for symlinks, as I said, I don't recommend a resolution on them. Or if you did implement it (I didn't check), make it customizable via a macro. This way if someone wants to track a symlink / shortcut intentionally to diferentiate something versus someone wanting to track a symlink / shortcut to make sure nobody altered the app, they have the full freedom to do so.

I'll check your progress when I get home from work.

P.S. I also got thinking about closed systems and either the dev can use a workaround like SDL Proprties for that, either they hardcode it (it's not like you can change or add a new executable on consoles anyway) so I don't think there is work needed there. If somebody does complain, a simple SDL_SetExeName() would suffice (to keep code portable and not fall back to workarounds like properties).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrOnlineCoder

Please check the above advice, also note the issue was auto-closed on libsdl, even though it was not fully implemented.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrOnlineCoder

Please check the above advice, also note the issue was auto-closed on libsdl, even though it was not fully implemented.

Copy link
Contributor Author

@MrOnlineCoder MrOnlineCoder Apr 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thedarknomad hey sorry for late reply, didn't have time to work on that one. What do you mean the issue was auto-closed? The #12615 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Process] [New Feature] Identifying current process
3 participants