44

How can I get the actual "username" without using the environment (getenv, ...) in a program? Environment is C/C++ with Linux.

Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
Zat42
  • 2,471
  • 4
  • 22
  • 36
  • 1
    Which is a difference between c and c++ function? Do you mean class method?? – AlexTheo Jan 21 '12 at 13:41
  • I just have to don't use c function – Zat42 Jan 21 '12 at 13:46
  • 8
    All of POSIX API is defined as C functions, which can be called all the same in C++. I don't understand your restriction. – drrlvn Jan 21 '12 at 13:52
  • 3
    This restriction doesn't make any sense... if you need to interact with a POSIX operating system you *have* to get down to C (or to assembly). And how do you define a "C++ function"? The C++ standard library doesn't provide anything for that, and even if it did it would just be a wrapper for `getlogin_r`. – Matteo Italia Jan 21 '12 at 13:55
  • Restriction ? Professors ! :) – Zat42 Jan 21 '12 at 13:56
  • For me, a C++ function is a function who don't need a C include (cstdlib, cstdio, cstring, ...) – Zat42 Jan 21 '12 at 13:59
  • 1
    @Armed9Gagger: I'm quite sure that he didn't mean what you said, because it wouldn't make any sense... can you tell us the exact words he wrote in the assignment? Probably he just doesn't you to write a C-style function (with `char *` & co.), but a C++ wrapper (with `std::string`, ...) for `getlogin_r`. – Matteo Italia Jan 21 '12 at 14:00
  • 2
    @Armed9Gagger: technically `cstdlib` & co. are C++ includes (providing the legacy C library inside the `std` namespace). Still, POSIX headers aren't included in standard C, are operating system APIs intended to be used from several languages (mainly C and C++). – Matteo Italia Jan 21 '12 at 14:02
  • I cant use the C standard library ! It's a restriction of my professors. – Zat42 Jan 21 '12 at 14:03
  • 1
    @Armed9Gagger: `getlogin_r()` - and in general all the POSIX APIs - is not in the C standard library. – Matteo Italia Jan 21 '12 at 14:10
  • No other possibility to get the username ? In a file on the proc/ ? I search but not find ... – Zat42 Jan 21 '12 at 14:10
  • If you cannot use any C function, you cannot achieve your homework. There is no C++ only standard library for that. Unless you use a C++ library like Qt. – Basile Starynkevitch Jan 21 '12 at 14:11
  • 1
    "C standard library" is different from "C functions". You need to use the operating system API (such as POSIX or WinAPI) which is provided as C functions and available in your compiler, it but doesn't belong to the standard library. You can use it and use this explanation for your professors. – Kos Jan 21 '12 at 14:11

7 Answers7

66

The function getlogin_r() defined in unistd.h returns the username. See man getlogin_r for more information.

Its signature is:

int getlogin_r(char *buf, size_t bufsize);

Needless to say, this function can just as easily be called in C or C++.

drrlvn
  • 8,189
  • 2
  • 43
  • 57
  • 7
    @Armed9Gagger, there is no API in the C++ library for these things which are defined in POSIX. C++ is *supposed* to use POSIX functions. – drrlvn Jan 21 '12 at 13:55
  • 12
    Note getlogin_r returns the name of the user logged into the controlling terminal of a process. This may be different than the user the job is running as. This function will also fail if you are not attached to a controlling terminal (e.g., when a process is daemonized). – Doug Richardson May 06 '14 at 23:52
  • 1
    getlogin() this is simpler – rohitsakala Aug 21 '15 at 11:57
  • 3
    This requires TTY to be present - if you're dispatching your applications through some kind of scheduler this will probably fail.. see Nemanja Boric's answer for a more "robust" version – ben.pere May 10 '16 at 08:15
59

From http://www.unix.com/programming/21041-getting-username-c-program-unix.html :

/* whoami.c */
#define _PROGRAM_NAME "whoami"
#include <stdlib.h>
#include <pwd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
  register struct passwd *pw;
  register uid_t uid;
  int c;

  uid = geteuid ();
  pw = getpwuid (uid);
  if (pw)
    {
      puts (pw->pw_name);
      exit (EXIT_SUCCESS);
    }
  fprintf (stderr,"%s: cannot find username for UID %u\n",
       _PROGRAM_NAME, (unsigned) uid);
  exit (EXIT_FAILURE);

}

Just take main lines and encapsulate it in class:

class Env{
    public:
    static std::string getUserName()
    {
        uid_t uid = geteuid ();
        struct passwd *pw = getpwuid (uid);
        if (pw)
        {
            return std::string(pw->pw_name);
        }
        return {};
    }
};

For C only:

const char *getUserName()
{
  uid_t uid = geteuid();
  struct passwd *pw = getpwuid(uid);
  if (pw)
  {
    return pw->pw_name;
  }

  return "";
}
Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
Nemanja Boric
  • 21,627
  • 6
  • 67
  • 91
  • 5
    Chances are that you can't use C *standard libraries*, not *all C functions*. You are required to directly or indirectly use C functions to perform your task. – Kos Jan 21 '12 at 14:14
  • To be pedantic, isn't your program missing a endpwent() call? – SO Stinks Dec 30 '15 at 17:36
  • @NemanjaBoric Can't you just use argv[0] to get the program name, even if it is changed after compilation? – The XGood Mar 03 '17 at 06:13
  • @TheXGood Ah, you mean for `_PROGRAM_NAME`. Yeah, that would do, this is just the example from the link I've pasted. – Nemanja Boric Mar 27 '17 at 10:00
  • 1
    @Dr.PersonPersonII "isn't your program missing a endpwent() call" - That's to be used after one or more calls to getpwent(), which the current and earlier versions of the reply didn't use AFAICT, so no. – Nick Aug 24 '19 at 15:18
3
#include <iostream>
#include <unistd.h>
int main()
{
    std::string Username = getlogin();
    std::cout << Username << std::endl;
    return 0 ;
}

Another way is this -

#include <iostream>
using namespace std;
int main()
{
       cout << system("whoami");
}
Akash
  • 139
  • 5
2

Use char *cuserid(char *s) found in stdio.h.

#include <stdio.h>

#define MAX_USERID_LENGTH 32

int main()
{
  char username[MAX_USERID_LENGTH];
  cuserid(username);
  printf("%s\n", username);
  return 0;
}

See for more details:

  1. https://pubs.opengroup.org/onlinepubs/007908799/xsh/cuserid.html
  2. https://serverfault.com/questions/294121/what-is-the-maximum-username-length-on-current-gnu-linux-systems
r3t40
  • 579
  • 6
  • 12
  • 1
    `cuserid()` [has been removed from the POSIX standard](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getlogin.html#tag_16_203_08): "The information provided by the cuserid() function, which was originally defined in the POSIX.1-1988 standard and subsequently removed, can be obtained by the following: `getpwuid(geteuid())`" – Andrew Henle Jul 05 '21 at 14:14
1

working a little bit through modern c++ specs

static auto whoAmI = [](){ struct passwd *tmp = getpwuid (geteuid ());
  return tmp ? tmp->pw_name : "onlyGodKnows"; 
}
juanba
  • 41
  • 4
  • 2
    any reason why this should be a lambda instead of a normal function/class? – Ankur S Oct 24 '20 at 16:07
  • In the context I was writing the code, a local function was the proper hiden.. IMHO a by hand class if nothing else is required could be avoided through functions(as your suggests)/lambdas which (among other things) it's nothing else than a executable object already built by the compiler. – juanba Oct 25 '20 at 17:40
0
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>

// Return username or nullptr if unknown.
const char* username() {
    const passwd* pw = getpwuid(geteuid());
    return pw == nullptr ? nullptr : pw->pw_name;
}
Timmmm
  • 88,195
  • 71
  • 364
  • 509
-2

Today I had to do the same thing, but didn't like to include any OS specific headers. So here is what you can do in a cross-platform way without resorting to any Linux/Windows specific headers:

#include <stdio.h> 
#include <memory>
#include <stdexcept>
#include <array>
#include <regex>

std::string execute_command(std::string cmd) 
{
    std::array<char, 128> buffer;
    std::string result;
    
    #if defined(_WIN32)
        #define POPEN _popen
        #define PCLOSE _pclose
    #elif defined(unix) || defined(__unix__) || defined(__unix)
        #define POPEN popen
        #define PCLOSE pclose
    #endif

    std::unique_ptr<FILE, decltype(&PCLOSE)> pipe(POPEN(cmd.c_str(), "r"), PCLOSE);
    if (!pipe) 
    {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) 
    {
        result += buffer.data();
    }
    return result;
}
std::string get_current_username()
{
    #if defined(_WIN32)
        // whoami works on windows as well but it returns the name 
        // in the format of `computer_name\user_name` so instead
        // we use %USERNAME% which gives us the exact username.
        #define USERNAME_QUERY "echo %USERNAME%" 
    #elif defined(unix) || defined(__unix__) || defined(__unix)
        #define USERNAME_QUERY "whoami"
    #endif
    auto username = execute_command(USERNAME_QUERY);
    // this line removes the white spaces (such as newline, etc)
    // from the username.
    username = std::regex_replace(username, std::regex("\\s"), "");
    return username;
    
}

This works just fine on both Linux and Windows and is C++11 compatible!

Test it online: https://paiza.io/projects/e/xmBuf3rD7MhYca02v5V2dw?theme=twilight

Hossein
  • 24,202
  • 35
  • 119
  • 224