Chinaunix首页 | 论坛 | 博客
  • 博客访问: 929011
  • 博文数量: 177
  • 博客积分: 8613
  • 博客等级: 中将
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-12 04:16
文章分类
文章存档

2012年(12)

2011年(24)

2010年(24)

2009年(75)

2008年(42)

我的朋友

分类: C/C++

2009-11-22 21:17:46

    刚才志远打电话来祝我生日快乐,心里很高兴,想想这么多年来我们志同道合,有相知如此,也不枉此生了。

 setuidgid这个程序只能用root来运行,很奇怪,我的系统上没有找到这个命令的man和程序,待会我装个和源码匹配的rpm包再试试。


/* setuidgid - run a command with the UID and GID of a specified user
程序的功能是用指定的用户的uid和gid来运行某个程序  */

/* Written by Jim Meyering --这哥们写了不少啊  */
//包含6个标准头文条件,其中getopt.h是我上午刚刚看过的,是用来解析命令行选项的头文件定义
#include
#include
#include
#include
#include
#include
//包含本文件夹里的头文件
#include "system.h"
//包含本软件包lib文件夹的3个头文件
#include "error.h"
#include "long-options.h"
#include "quote.h"

#define PROGRAM_NAME "setuidgid" //定义宏PROGRAM_NAME,其值是一个字符串

/* I wrote this program from scratch, based on the description of
   D.J. Bernstein's program: */
#define AUTHORS "Jim Meyering"//定义宏AUTHORS,这些宏,程序名、作者都是标准做法,给man等程序用的

#define SETUIDGID_FAILURE 111//定义宏SETUIDGID_FAILURE,其值是字面值常量,111,具体作用还是得在程序里具体看

char *program_name;//声明字符指针变量program_name

void
usage (int status)//帮助函数,接受一个整型形参
/*
将来好几十k的大程序,甚至包含好多文件的工程,向这样一行行的写注释,估计不是个好办法,给一个函数写注释,
还可以,现在是精读,日后就是快速阅读了,期待ing
*/
{
  if (status != EXIT_SUCCESS)//如果传入的形参不等于EXIT_SUCCESS,则向标准错误输出一串错误信息,注意fprintf()函数的使用,将来肯定用的到
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else//否则,如果传入的形参等于EXIT_SUCCESS,则打印下面这一大片信息
    {
      printf (_("\
Usage: %s USERNAME COMMAND [ARGUMENT]...\n\
  or:  %s OPTION\n\
"),
          program_name, program_name);

      fputs (_("\
Drop any supplemental groups, assume the user-ID and group-ID of\n\
the specified USERNAME, and run COMMAND with any specified ARGUMENTs.\n\
Exit with status 111 if unable to assume the required user and group ID.\n\
Otherwise, exit with the exit status of COMMAND.\n\
This program is useful only when run by root (user ID zero).\n\
\n\
"), stdout);//写的挺清楚的,丢弃任何补充的组信息,使用给定的用户名和组来运行程序,如果设置用户名和组失败,返回111,否则返回调用程序的返回值,这个程序对root来说很有用
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);//再次注意,调用usage帮助函数后,不是return继续运行,而是直接跳出了程序。
}

int
main (int argc, char **argv)//标准的main函数,当然,形参还可以这一写,int argc, char *argv[]
{
  char const *user_id;//声明一个指向字符常量的指针user_id
  struct passwd *pwd;//声明一个指向passwd结构体的指针,pwd,这个结构体好像是对应于/etc/passwd文件的那种格式,记得以前在哪个程序里看到过的
//下面这5句是标准的可移植的做法,不再复述
  initialize_main (&argc, &argv);
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  initialize_exit_failure (SETUIDGID_FAILURE);//调用system.h里面定义的宏initialize_exit_failure
  atexit (close_stdout);//登记出口函数

/*
在这个地方详细地追踪一下命令行解析的整个过程,首先,parse_long_options这个函数是在lib/long-options.h中声明的唯一一个函数,改头文件在程序开始的地方被包含
ok,看看long-options.h里声明的parse_long_options这个函数具体的实现吧:
此函数在lib/long-options.c中实现,在那个文件里我写了详细的注释,
最后的结果是,如果argc是2,并且命令行参数的第一个是-h/-v则调用帮助函数,否则啥也不做
*/
  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              usage, AUTHORS, (char const *) NULL);
//此处调用getopt_long库函数,解析一次命令行参数,如果有参数,则调用帮助函数,这里说明这个函数是不需要任何命令行选项的,需要的是直接写在程序名后面的参数
  if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
    usage (SETUIDGID_FAILURE);

  if (argc <= optind + 1)//调用getopt_long库函数后,会设置一个全局变量optind,指向命令行中所有选项之后的第一个参数的位置,如果argc的值小于或者等于optind+1的值,则说明有问题,具体是这样的:
    {
      if (argc < optind + 1)//这种情况是,argc的值是1,optind的值也是1,则1<2,说明用户只写了程序名,没有任何的操作数,肯定是错误的
    error (0, 0, _("missing operand"));
      else//相等的情况也不对
    error (0, 0, _("missing operand after %s"), quote (argv[optind]));
      usage (SETUIDGID_FAILURE);
    }

  user_id = argv[optind];//user_id这时候得到了一个值,这个值是命令行中第一个参数字符串的首地址
  pwd = getpwnam (user_id);//调用getpwnam库函数,目的是得到这个uid指向的那个passwd结构体,说白了,就是找到/etc/passwd里面对应的那一行
  if (pwd == NULL)//如果getpwnam库函数没有找到对应的/etc/passwd的项,或者有错误发生的情况下,才返回NULL空指针,这时候程序就调用error函数了,打印的时候说明没有找到这个uid
    error (SETUIDGID_FAILURE, errno,
       _("unknown user-ID: %s"), quote (user_id));

#if HAVE_SETGROUPS //条件编译,如果宏HAVE_SETGROUPS的值不是0,则执行以下的代码
  if (setgroups (1, &pwd->pw_gid))//为当前进程设置一个组id
    error (SETUIDGID_FAILURE, errno, _("cannot set supplemental group"));//这就是经验,在调用setgroups这样的系统调用的时候,也进行了判断,如果系统调用失败,则调用error函数,告诉用户发生了什么,这说明并不是所有的系统调用也是不会失败的,一会我测试一下,不用root用户,估计就会失败
#endif//条件编译结束

  if (setgid (pwd->pw_gid))//调用系统调用setgid,设置当前进程的有效gid,如果系统调用失败,同样调用error函数
    error (SETUIDGID_FAILURE, errno,
       _("cannot set group-ID to %lu"), (unsigned long int) pwd->pw_gid);

  if (setuid (pwd->pw_uid))//通setgid一样,setuid设置当前进程的有效uid,如果此系统调用失败,也调用error函数
    error (SETUIDGID_FAILURE, errno,
       _("cannot set user-ID to %lu"), (unsigned long int) pwd->pw_uid);

  {//这个语句块来实际地执行程序
    char **cmd = argv + optind + 1;//cmd是一个指向字符指针的指针,得到的值是argv+optind+1的值,也就是uid后面的那个字符串的首地址
    int exit_status;//声明整型变量exit_status ,用来保存退出状态
    execvp (*cmd, cmd);//调用库函数,运行这个程序
    exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);//通过一个条件语句得到退出状态

    error (0, errno, _("cannot run command %s"), quote (*cmd));//在这里,说明有错误发生了,否则不会返回值
    exit (exit_status);//退出程序
  }
}

阅读(2036) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~