Идентификатор метода C и getpwnam(3)

Для моего IT в школе я должен сделать программу, которая выводит идентификатор пользователя и идентификатор группы пользователей точно так же, как команда "id", просто без групп. Это работает нормально для меня, но теперь я должен использовать метод getpwnam для создания расширенной версии: его следует использовать как ./id_extended USERNAMEи тогда он должен вывести uid и gid этого пользователя. Я взглянул на getpwnam(3), потому что урок сказал мне, чтобы искать там, чтобы сделать это. Теперь есть пример использования getpwnam_r с буфером и т. д., но мы не должны это делать; мы должны использовать простую getpwnam,

я знаю это getpwnam возвращает указатель, но как этот указатель можно использовать для идентификатора группы пользователей и идентификатора пользователя?

1 ответ

ОП должен был включить свой код в вопрос. Давайте рассмотрим это по частям:

#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

Поскольку это код POSIX.1 C, код должен начинаться с #define _POSIX_C_SOURCE 200809L сообщить заголовочным файлам библиотеки C, что нам нужны функции POSIX.1-2008.

getpwnam() требует #include <pwd.h> но также #include <sys/types.h>, которого нет в списке.

Если используется, getuid() а также getgid() требовать #include <unistd.h>, который указан.

Требуется стандартный ввод / вывод #include <stdio.h>, а также #include <errno.h> обеспечивает errno и коды ошибок. Я бы лично тоже #include <string.h> за strerror()потому что я люблю использовать fprintf() печатать сообщения об ошибках в моем собственном формате (вместо того, чтобы использовать, например, perror() от <stdio.h>, или %m Расширение GNU.

Следующая часть,

struct passwd *getpwnam(const char *name);
uid_t getuid(void);
uid_t geteuid(void);
struct passwd pass;

довольно проблематично: во-первых, вам не нужно объявлять функции библиотеки C; заголовочные файлы объявляют их для нас уже. Во-вторых, возвращаемое значение из getpwnam() это указатель на struct passwd -- это то что struct passwd * средства! - объявляю struct passwd pass имеет нулевой смысл. В-третьих, если pass используется только в main(), он должен быть объявлен как локальная переменная в main(), а не как глобальная переменная (вне какой-либо функции).

В последней части

int main(int argc,char* argv[]) {
    if(argc == 1){
    printf("user: %d\ngroup: %d\n",getuid(),getgid());
    }
    if(argc == 2){
    pass = getpwnam(argv[1]);
     printf("%s   %ld",pass.pw_uid,pass.pw_gid );
    }
}

Мы видим, что main() правильно определено (как принятие int argc а также char *argv[]); Тем не менее, нет return (some int) Заявление декларация требует. Это лучше всего исправить, добавив return EXIT_SUCCESS; в конце.

поскольку pass используется только в main(), это должна быть локальная переменная, и, следовательно, тело main() следует начать с правильного объявления: struct passwd *pass;,

Программа ничего не делает, если вы предоставите два или более аргумента. Это очень удивительно и не желательно. if (argc == 1) { /* no arguments */ } else if (argc == 2) { /* one argument */ } else { /* two or more arguments */ } с сообщением об ошибке, напечатанным в случае слишком большого количества аргументов, было бы намного более разумным выбором здесь.

В случае с одним аргументом (argc == 2 - запомни это argc включает в себя argv[0] (это имя используется для выполнения этой программы), есть две отдельные проблемы: во-первых, pass не является указателем (который можно исправить, объявив его так, как показано выше). Во-вторых, и более важным моментом является то, что нет проверки, если getpwnam() удалось или не удалось.

В случае ошибки, man 3 getpwnam говорит нам, что getpwnam() вернусь NULL если происходит ошибка, с errno описывая ошибку. Это означает, что если pass правильно объявлено, вы должны использовать, например,

    pass = getpwnam(argv[1]);
    if (!pass) {
        fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
        return EXIT_FAILURE;
    }

    /* pass->pw_uid has user ID,
       pass->pw_gid has group ID,
       pass->pw_name has user name,
       and so on. */ 

Обратите внимание, что if (!pass) эквивалентно if (pass == NULL) в POSIX.1 C.

(Использование более короткой формы - это мое личное предпочтение. ! это не оператор, я на самом деле читал первый как "если нет pass "; тогда я прочитал второй как " если pass NULL ". Бывший просто легче / быстрее / удобнее для меня.)

Также обратите внимание, что нет спецификатора формата printf для uid_t или же gid_t, Хотя большинство кода использует int (%d) и предполагает, что компилятор будет обрабатывать продвижение правильно, я лично предпочитаю использовать long (%ld) и приведите результат к long явно; например,

    printf("uid=%ld (%s)\n", (long)(pass->pw_uid), pass->pw_name);

(Чаще всего вы видите актерский состав, написанный как (long)pass->pw_uid, но если вы не помните правила приоритета операторов в C, то может быть неясно, какая часть выражения приведена к long, Чтобы было ясно для нас, людей, читающих код, компилятору точно все равно! - Мне нравится иногда писать это как (long)(pass->pw_uid) в таком случае (pass->pw_uid) т.е. значение pw_uid поле в структуре, на которую указывает pass.)

Наконец, есть два изменения в логике программы, которые я хотел бы предложить.

Вместо использования struct passwd *pass если было указано имя пользователя, я бы использовал его также, когда имя пользователя не указано, для получения информации о текущем пользователе с помощью getpwuid(getuid()), Затем код, который печатает информацию в структуре, на которую указывает pass, мог остаться вне тела if (argc == ..) статьи. Таким образом, есть только один printf() распечатать информацию, и она всегда будет одной и той же формы, независимо от того, печатается ли информация для текущего пользователя или какого-либо другого пользователя, указанного по имени.

(getuid() а также getgid() всегда успешны в POSIX.1; они не могут потерпеть неудачу. Вот почему совершенно нормально использовать их без какой-либо проверки ошибок.)

Второе изменение больше похоже на дополнение. Вы могли бы использовать getgrgid(pass->pw_uid) или же getgrgid(getgid()) получить указатель на struct group который предоставляет аналогичную информацию о группе пользователей; особенно ->gr_name, который является названием группы. Использовать getgrgid() Вы также должны #include <grp.h> в начале файла. (Это также требует #include <sys/types.h>, но вы уже должны иметь это, так как getpwnam() а также getpwuid() требовать.)

Приведенной выше информации должно быть достаточно для написания полностью работающей программы в соответствии с заявленными требованиями. Однако я не буду перечислять полную исправленную / измененную программу, потому что вы узнаете больше и получите удовольствие от самостоятельного завершения своей работы - и мы все избегаем того, чтобы халявщики могли использовать наши усилия, представляя нашу работу как свою. своя.

Другие вопросы по тегам