Идентификатор метода 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()
требовать.)
Приведенной выше информации должно быть достаточно для написания полностью работающей программы в соответствии с заявленными требованиями. Однако я не буду перечислять полную исправленную / измененную программу, потому что вы узнаете больше и получите удовольствие от самостоятельного завершения своей работы - и мы все избегаем того, чтобы халявщики могли использовать наши усилия, представляя нашу работу как свою. своя.