Alternative way to obtain argc and argv of a process

13,410

Solution 1

On Linux, you can get this information from the process's proc file system, namely /proc/$$/cmdline:

int pid = getpid();
char fname[PATH_MAX];
char cmdline[ARG_MAX];
snprintf(fname, sizeof fname, "/proc/%d/cmdline", pid);
FILE *fp = fopen(fname);
fgets(cmdline, sizeof cmdline, fp);
// the arguments are in cmdline

Solution 2

The arguments to main are defined by the C runtime, and the only standard/portable way to obtain the command line arguments. Don't fight the system. :)

If all you want to do is to provide access to command line parameters in other parts of the program with your own API, there are many ways to do so. Just initialise your custom class using argv/argc in main and from that point onward you can ignore them and use your own API. The singleton pattern is great for this sort of thing.

To illustrate, one of the most popular C++ frameworks, Qt uses this mechanism:

int main(int argc, char* argv[])
{
    QCoreApplication app(argc, argv);

    std::cout << app.arguments().at(0) << std::endl;

    return app.exec();
}

The arguments are captured by the app and copied into a QStringList. See QCoreApplication::arguments() for more details.

Similarly, Cocoa on the Mac has a special function which captures the command line arguments and makes them available to the framework:

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
    return NSApplicationMain(argc, (const char **)argv);
}

The arguments are then available anywhere in the app using the NSProcessInfo.arguments property.

I notice in your updated question that your class directly stores a copy of argc/argv verbatim in its instance:

int const argc_;
char const** const argv_;

While this should be safe (the lifetime of the argv pointers should be valid for the full lifetime of the process), it is not very C++-like. Consider creating a vector of strings (std::vector<std::string>) as a container and copy the strings in. Then they can even be safely mutable (if you want!).

I want to make a class that is independent of main() so that argc and argv don't have to be passed explicitly to the code that uses them.

It is not clear why passing this info from main is somehow a bad thing that is to be avoided. This is just how the major frameworks do it.

I suggest you look at using a singleton to ensure there is only one instance of your Application class. The arguments can be passed in via main but no other code need know or care that this is where they came from.

And if you really want to hide the fact that main's arguments are being passed to your Application constructor, you can hide them with a macro.

Solution 3

To answer the question in part, concerning Windows, the command line can be obtained as the return of the GetCommandLine function, which is documented here, without explicit access to the arguments of the main function.

Solution 4

I totally agree with @gavinb and others. You really should use the arguments from main and store them or pass them where you need them. That's the only portable way.

However, for educational purposes only, the following works for me with clang on OS X and gcc on Linux:

#include <stdio.h>

__attribute__((constructor)) void stuff(int argc, char **argv)
{
    for (int i=0; i<argc; i++) {
        printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
    }
}

int main(int argc, char **argv)
{
    for (int i=0; i<argc; i++) {
        printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
    }
    return 0;
}

which will output:

$ gcc -std=c99 -o test test.c && ./test this will also get you the arguments
stuff: argv[0] = './test'
stuff: argv[1] = 'this'
stuff: argv[2] = 'will'
stuff: argv[3] = 'also'
stuff: argv[4] = 'get'
stuff: argv[5] = 'you'
stuff: argv[6] = 'the'
stuff: argv[7] = 'arguments'
main: argv[0] = './test'
main: argv[1] = 'this'
main: argv[2] = 'will'
main: argv[3] = 'also'
main: argv[4] = 'get'
main: argv[5] = 'you'
main: argv[6] = 'the'
main: argv[7] = 'arguments'

The reason is because the stuff function is marked as __attribute__((constructor)) which will run it when the current library is loaded by the dynamic linker. That means in the main program it will run even before main and have a similar environment. Therefore, you're able to get the arguments.

But let me repeat: This is for educational purposes only and shouldn't be used in any production code. It won't be portable and might break at any point in time without warning.

Solution 5

In Windows, if you need to get the arguments as wchar_t *, you can use CommandLineToArgvW():

int main()
{
    LPWSTR *sz_arglist;
    int n_args;
    int result;
    sz_arglist = CommandLineToArgvW(GetCommandLineW(), &n_args);
    if (sz_arglist == NULL)
    {
        fprintf(stderr, _("CommandLineToArgvW() failed.\n"));
        return 1;
    }
    else
    {
        result = wmain(n_args, sz_arglist);
    }
    LocalFree(sz_arglist);
    return result;
}

This is very convenient when using MinGW because gcc does not recognize int _wmain(int, wchar_t *) as a valid main prototype.

Share:
13,410

Related videos on Youtube

user1095108
Author by

user1095108

Updated on June 14, 2022

Comments

  • user1095108
    user1095108 almost 2 years

    I'm looking for alternative ways to obtain the command line parameters argc and argv provided to a process without having direct access to the variables passed into main().

    I want to make a class that is independent of main() so that argc and argv don't have to be passed explicitly to the code that uses them.

    EDIT: Some clarification seems to be in order. I have this class.

    class Application
    {
      int const argc_;
      char const** const argv_;
    
    public:
      explicit Application(int, char const*[]);
    };
    
    Application::Application(int const argc, char const* argv[]) :
      argc_(argc),
      argv_(argv)
    {
    }
    

    But I'd like a default constructor Application::Application(), with some (most probably) C code, that pulls argc and argv from somewhere.

    • user3078414
      user3078414 about 8 years
      What do you mean with "obtain"? Where else from?
    • Some programmer dude
      Some programmer dude about 8 years
      There's no portable or standard way of getting arguments to the program except the arguments to main.
    • user1095108
      user1095108 about 8 years
      @PaulR yes, yes, the APIs are in C, as you know, but class is in C++. I didn't want an answer featuring python, even though it would be cool.
    • user1095108
      user1095108 about 8 years
      @JoachimPileborg True, but the question is OS-specific, non-portable.
    • Some programmer dude
      Some programmer dude about 8 years
      As for the user not needing to pass argc and argv to the "class", take a look at just about all portable and platform-independent GUI frameworks, they all needs the user of the framework to pass argc and argv to the framework explicitly. So it's common and not something programmers are unused to.
    • Some programmer dude
      Some programmer dude about 8 years
      You tag this question linux, windows, posix and bsd and say this question is OS-specific? If it was OS-specific you would mention only one OS, the one you target.
    • user1095108
      user1095108 about 8 years
      @JoachimPileborg The frameworks contain OS-specific code, not just portable code, there is not reason whatsoever why not make the command line gathering part OS-specific too. I'm trying to gather as many ways to gather the command line as possible, but not infinite and not opinion-based.
    • CoffeDeveloper
      CoffeDeveloper about 8 years
      chicken-egg problem, if you want a code that provides argument, then that code have to rely on main, directly or indirectly you will have a main.
    • CoffeDeveloper
      CoffeDeveloper about 8 years
      We should stop upvoting ill-formed questions before getting the question right. The question is still not clear to me, you basically want to get those parameters, without the need to pass them? You can create a wrapper that if injected provides parameters indirectly. I just think that once requirements of OP are clear, then we can just proof if what he's want is even possible
  • Admin
    Admin about 8 years
    1. you can read /proc/self/cmdline 2. args could have been destroyed 3. it is super odd to deal with /proc files with FILE abstraction 4. ARG_MAX is only a limit for one argument, not all args in total 5. missing error checks, arguably could be omitted in the example. However, the biggest issue is that OP is likely trying to do something wrong and such answer like this should not be posted in the first place without further clarification.
  • Cory Klein
    Cory Klein about 8 years
    This is a popular answer. You may consider adding an example in-line of how to use GetCommandLine.
  • user1095108
    user1095108 about 8 years
    That's exactly what I don't want to do.
  • CoffeDeveloper
    CoffeDeveloper about 8 years
    So you want a getter for a global variable, but you don't want the global variable eh?
  • jamesqf
    jamesqf about 8 years
    But the OP states that this is in C, so there are no classes. You could pass them to an init function, and have the values stored in static variables in the module code.
  • Luaan
    Luaan about 8 years
    @jamesqf Well, I'm just using the same name the OP used :) Passing the values is the important part, not how exactly it is implemented.
  • sfdcfox
    sfdcfox about 8 years
    Classes in C? Say it ain't so!
  • isanae
    isanae about 8 years
  • T.E.D.
    T.E.D. about 8 years
    Upvoting this. The typical method I see used for API's that want CL access to parse their own arguments (eg: QT) is to just ask for argv and argc. The nice thing about using the standard idiom is that experienced programmers will know what you are doing at a glance.
  • Noah Spurrier
    Noah Spurrier about 8 years
    I believe that /proc/*/cmdline is truncated to some max length constant. This is not a limitation of the size of the process command-line that the OS can start. Instead, this is a limitation of the process list record keeping in Linux. So you can start a process with a longer list of arguments, but the kernel is not going to remember them all for the purpose of trying to read arguments from the process list.
  • Admin
    Admin about 8 years
    The kernel does not memorize arguments. Instead it stores addresses for both start and end of said args and then reads them from target process address space. I don't see any code truncating the result either (unless the requested amount is too small of course). lxr.free-electrons.com/source/fs/proc/base.c#L199
  • Patrick Collins
    Patrick Collins about 8 years
    That's pretty nifty. Are __attribute__((constructor)) functions guaranteed to have access to argv/argc, or does this just abuse some accident of the representation?
  • Matthieu M.
    Matthieu M. about 8 years
    I understand the idea of using a global (it's ugly, but it does the job done), however WHY enforcing uniqueness? What's wrong with allowing anyone to create a fake set of arguments and pass those instead?
  • Johannes Weiss
    Johannes Weiss about 8 years
    It's not guaranteed at all.
  • user1095108
    user1095108 about 8 years
    It's exactly what I don't want. It may be wrong to find argc and argv using alternative means, but why should people be discouraged from doing so anyway? There are situations apart from the one I described, where it seems to be useful to be able to do so.
  • gavinb
    gavinb about 8 years
    @MatthieuM. The singleton is merely to preserve the same semantics as the data it encapsulates; there is one set of read-only arguments. Not a strong requirement.
  • T.E.D.
    T.E.D. about 8 years
    From a strategic point of view, many SE users consider any singleton use to be code smell, and will downvote an otherwise good answer just for mentioning them. As someone who upvoted this answer, I think that would be a shame. If its not central to your answer, no point in suggesting something controversial.
  • gavinb
    gavinb about 8 years
    @user1095108 You say that you don't want to get access the arguments the standard way, but not why. Accessing the arguments from another part of the application is possible, regardless of how they are obtained in the first place. If you don't want to use the defined mechanism, you would need to circumvent the C runtime startup code, which opens a whole can of worms.
  • saagarjha
    saagarjha about 6 years
    @gavinb Interestingly, on macOS, NSApplicationMain actually ignores the passed in argc and argv and gets it directly from _NSGetArgc and _NSGetArgv.
  • gavinb
    gavinb about 6 years
    @SaagarJha Interesting, yes I just looked up the implantation in opensource.apple.com/source/Libc/Libc-763.13/sys/… . It relies on very specific and detailed knowledge of dyld and libSystem internals.
  • Evgen
    Evgen about 5 years
    Trying to use this, but only getting the command name (with path, if was explicitly specified). So cat /proc/self/cmdline x y z returns cat/proc/self/cmdlinexyz, but fgets on that file returns just cat. Why is that?
  • Evgen
    Evgen about 5 years
    (answering myself) - Turns out the args are NULL-separated. Can't just use a string :(
  • saagarjha
    saagarjha over 4 years
    glibc and dyld implement this extension; musl does not. Take of that what you will.
  • Sergey Kalinichenko
    Sergey Kalinichenko over 2 years
    This is such a hack... Such an amazingly beautiful, delightful hack!