# Linux碎点

> 作者：zning（https://ning.ae） · 发布：2015/04/06 13:43 下午 (UTC+8) · 原文：https://zning.me/14860141934578.html

**Linux下如何编译并运行C程序**

GCC是Linux操作系统下一个非常重要的源代码编译工具,有着许多重要的选项,支持许多不同语言的编译,如C、C++、Ada、Fortran、Objective、Perl、Python、Ruby以及Java等,甚至Linux的许多内核和许多其他自由软件以及开放源码应用程序都是用C语言编写并经gcc编译而成的.

1.编译单个源文件

[例如]在屏幕上打印”Hello,Linux.”

[源代码]

#include

#include

int main(int argc,char **argv)

{

printf(“Hello,Linux.\n”);

exit(0);

}

将源文件保存为hello.c,开始进行编译

$gcc -o hello hello.c

编译成功完成后,在当前路径下,生成一个名为hello的文件,然后执行

$./hello

在屏幕上,你将会看到打印结果:Hello,Linux.

说明:在默认情况下,编译成功完成后,会在当前路径下,生成一个名为a.out的文件,然后执行$./a.out便可打印结果,但通常可以通过选项-o来指定自己的可执行程序名称;

2.编译多个源文件

3.使用外部函数库

4.使用共享函数库和静态函数库

5.gcc选项详细描述

**Linux普通用户无法使用sudo处理及sudoers设置**

http://www.tuicool.com/articles/bYvqYfn

Linux下我们经常使用的SSH远程连接到系统进行操作，而为了安全，一般使用的是非root用户连接，只有在必要的时候，我们才会使用sudo切换到root帐户下进行操作，以保证系统尽可能的安全和不被损坏。然而，在有的时候我们通过普通用户SSH连接到Linux的时候，使用sudo来执行身份切换的时候，系统会报错（以下是CentOS6.4下的报错）：

对不起，用户 yourusername 不能在 CentOS 上运行 sudo。

或者

Sorry,user yourusername may not run sudo on CentOS

这种情况下，为了让我们的用户能够正常的使用sudo，需要通过修改 `/etc/sudoers` 文件。

#添加下面一行到:/etc/sudoers，即可让该用户使用sudo
yourusername ALL=(ALL) ALL

这种修改是将所有的权限都赋值给了yourusername，为了sudo这个命令，就这么ALL几下，似乎有点不负责任啊,有木有？

sudoers文件默认是只读的，因此不能直接修改。网上很多方法是使用chmod来修改sudoers的权限后再通过编辑器（vi/vim etc.）来修改sudoers内容，之后再将sudoers文件的权限恢复，来达到修改sudoers的目的。这种方法确实能够修改sudoers，但是却没有办法保证sudoers文件的正确性，如果修改出现错误，将导致sudoers（sudo）不能正常的工作。更正确的做法是直接使用sudoers的专用编辑器： `visudo` 来编辑sudoers文件。

# visudo

## visudo默认的编辑器是vi，如果你需要修改你的默认编辑器，可以通过添加EDITOR="" 来修改。

# EDITOR="/usr/bin/vim -p -X" visudo

## 如果要永久改变默认编辑器，只要将EDITOR设置为环境变量

# export EDITOR="/usr/bin/vim -p -X"

使用 `visudo` 来编辑sudoers的好处是：当我们完成sudoers的编辑时，系统会自动检测我们sudoers的格式是否正确，从而保证sudoers文件的正确性。

其实，sudoers文件是非常简单的，其内容只包含两种，第一种是别名（Aliases，基本变量定义），第二种是用户规范描述（user specifications，指定某用户可以运行的内容）。而这两种内容都是通过 *扩展巴科斯范式（EBNF）* 1 来进行定义。一旦定义了别名，在用户说明中，就可以使用别名来简化给用户定义运行内容。如果同一个用户在sudoers文件中能够找到多条说明，那么将使用找到的最后一条。

sudoers的别名包含四种：User_Alias，Runas_Alias，Host_Alias和Cmnd_Alias。

User_Alias是用户别名，这个和系统中的group有点类似。通过用户别名，在sudoers中，可以将一个或者多个用户用别名来替代。

## 定义一个用户别名
User_Alias OPERATORS = operator1,operator2,remote_admin

注意：
1\. 等号后面的都是系统中的用户名,用户ID，组，组id等组成，多个用逗号分隔，具体信息可参看文后参考内容 *sudoers 2*
2\. 别名只能是大写字母开头的，并由大写字母，下划线和数字组成的字符串

Runas_Alias和User_Alias类似，但不同的是Runas_Alias指明的是用户将要运行命令的身份。Runas_Alias可以包含Runas_Alias别名（User_Alias是包含User_Alias别名）。

同时要注意的是，上面两个别名在匹配的时候使用的是字符串匹配法则，也就是说，当两个不同用户名的用户，即使使用了同一个用户ID，在实际情况下，也会被认为是不同的。要避免这种情况，可以考虑使用用户的ID来替代用户名。如：

Runas_Alias ROOT = #0
## 所有的用户ID为0的，统归为ROOT别名

Host_Alias是主机别名，指定规则适用的主机列表。

Host_Alias SERVERS = 192.168.0.1, 192.168.0.2, server1

注意：

1\. Host_Alias中，只能使用真实的地址，这就意味着127.0.0.1这种地址是永远不会被匹配到的。而localhost要只有在真有主机的主机名为localhost的时候才能被匹配。

2\. 如果IP地址没有给定掩码，sudo自动会搜寻本地网卡上对应匹配的掩码地址来作为默认的网络掩码。

Cmnd_Alias是命令别名，指定用户能够运行的某一类命令的列表。可包含一个，或多个命令（带参数），目录，或者其他的命令别名。

Cmnd_Alias SHUTDOWN_CMDS = /sbin/poweroff, /sbin/reboot, /sbin/halt
##所有的关机命令
Cmnd_Alias ADMIN_CMDS = /usr/sbin/passwd, /usr/sbin/useradd, /usr/sbin/userdel, /usr/sbin/usermod, /usr/sbin/visudo
## 所有的用户管理命令

注意：

1\. 命令列表中如果包含目录（以/结束），那么该目录下（不含子目录）的所有可执行文件都被赋予了执行权限，这和使用通配符*类似；

2\. 命令列表中务必使用全路径名，如果不使用完全限定名，将意味着恶意用户可以创建同名文件并在切换后的用户下运行，这将给系统带来未知的安全风险；

3\. 当命令列表中的命令需要带参数的时候，如果出现特殊字符（’,’,’:’,’=’,’\’），将需要使用反斜杆（’\’）进行转义。

通过上面四种别名，我们能够定义出特定的用户，主机和命令列表，在实际运行中，我们如果要对这些特定的列表的某些运行时 *配置项* 3 进行修改，我们可以通过Default来完成。

Default对于前面四种别名的限制格式各有不同，定义如下：

## EBNF
Default_Entry ::= ('Defaults' |'Default' '@' Host_List |'Default' ':' User_List | 'Default' '!' Cmnd_List | 'Default' '>' Runas_List ) Parameter_List

Parameter_List ::= (Parameter '=' Value |Parameter '+=' Value |Parameter '-=' Value |'!'* Parameter)[ , Parameter_List]

定义中 *_List可以是别名也可以是特定的具体信息（如：用户名）。

别名之后，就是具体的用户规范描述（User Specification）了。

User Specification表述的主要意义就是： **哪些用户在哪些环境里可以作为哪些用户来运行哪些程序** 。用EBNF来表示这个定义如下：

User_Spec ::= User_List Host_List '=' (Cmnd_Spec | Cmnd_Spec ',' Cmnd_Spec_List) \
        (':' Host_List '=' Cmnd_Spec_List)*

Cmnd_Spec ::= Runas_Spec? SELinux_Spec? Solaris_Priv_Spec? Tag_Spec* Cmnd

Runas_Spec ::= '(' Runas_List? (':' Runas_List)? ')'

SELinux_Spec ::= ('ROLE=role' | 'TYPE=type')

Solaris_Priv_Spec ::= ('PRIVS=privset' | 'LIMITPRIVS=privset')

Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
              'SETENV:' | 'NOSETENV:' | 'LOG_INPUT:' | 'NOLOG_INPUT:' |
              'LOG_OUTPUT:' | 'NOLOG_OUTPUT:')

看起来很复杂，来个栗子也许会好很多：

## 在SERVERS环境中OPERATORS可以以ROOT角色运行SHUTDOWN_CMDS
OPERATORS SERVERS = (ROOT) SHUTDOWN_CMDS

## 在所有的环境中yourusername都可以切换到所有账户下运行所有的命令
yourusername ALL = (ALL) ALL

## yourusername用户在SERVERS上面执行关机操作
yourusername SERVERS = /sbin/halt, /sbin/poweroff

## 以root来执行关机操作，但以operator来执行kill命令
yourusername SERVERS = (root) SHUTDOWN_CMDS, (operator) /bin/kill
## sudo -u来选择用户

## 仅以operator组来执行kill命令
yourusername SERVERS = (:operator) /bin/kill
## 注意，在sudo的时候需要使用-g配合，但kill还是由yourusername执行

## 无需使用密码(不用校验operator身份)，即可以operator身份来执行kill命令
yourusername SERVERS = (operator) NOPASSWD: /bin/kill

至于SELinux_Spec和Solaris_Priv_Spec一般不会用到，SELinux_Spec是在支持SELinux的系统中有效，而Solaris_Priv_Spce则是针对Solaris系统的配置。Tag_Spec相对前两个在Linux系统来说就实用的多，Tag_Spec共有10个可选tag参数，可以分成5组，每组由互斥的两个tag参数构成，分别是：NOPASSWD和PASSWD，NOEXEC和EXEC，SETENV和NOSETENV，LOG_INPUT和NOLOG_INPUT，LOG_OUTPUT和NOLOG_OUTPUT。不同组别的tag参数可以同时使用，在使用Tag_Spec之后，如果没有使用相反的tag参数覆盖，则同行后面的命令将会继承tag参数设置。下面是5组tag的说明：

| Tag参数 | 作用描述 |
| NOPASSWD,PASSWD | 标识是否需要身份验证 |
| NOEXEC，EXEC | 是否限制程序进一步运行命令（动态链接有效） |
| SETENV，NOSETENV | 标志如何处理环境变量，建议不用SETENV（默认是NOSETENV），SETENV会禁用通过命令行传递的env_reset参数 |
| LOG_INPUT,NOLOG_INPUT | 输入日志开启标志，LOG_INPUT开启后，所有输入信息都会被记录，如密码这类敏感信息同样会以明文形式记录下来 |
| LOG_OUTPUT,NOLOG_OUTPUT | 输出日志标志，开启后，所有的输出都会被保存在文件中 |

到此为止，sudoers文件的主体就已经完成了，根据这些东西，你就可以顺利修改sudoers了。

高级部分：

1\. 可以通过#includedir，#include两中方式将其他的sudoers文件包含进来；

2\. 要注释，用#，当然，第一点中的这个和#后面跟数字的要小心（猜猜看,是神码？）；

3\. 在编辑中，为了简便，你可以在主机列表，路径名称和命令列表中使用通配符（不是正则），通配符：*（匹配0或多个），?（匹配单个字符），[…]（匹配范围内的任一字符），[!…]（说了不是正则了，匹配任一不在指定范围内的字符），\x（转义特殊字符，如：*，?, [, ]）。例外：””双引号在命令行参数中表示的是命令不允许带任何参数；传递给sudoedit内置命令的命令行参数必须包含路径名称，因此斜杆（/）不会被通配符匹配；

4\. 在Alias，Defaulsts，User Specification中设定值的时候可以使用感叹号（!），表示取非(排除)，当然，如果你觉得多几个感叹号能加强效果也是可以的，记住单数表示取反，偶数是双重否定，直接抵消作用即可。同时也要注意，这个感叹号有时候会失效，会带来一些安全风险；

5\. 上一点的一点特例:

## 可Runas除了root之外的所有用户
User_Alias SPECIALUSERS = ALL,!root

## 这个的作用不同于上面这行规则，sudo匹配的时候是不匹配root
## 同时也不会匹配任何其他用户
User_Alias SPECIALUSERS = !root

6\. 规则太长，一行写不下？试试行尾加上反斜杆（\），然后换到第二行继续写。

7\. MitchellChu本身也是菜鸟一枚，共勉！ （这个最高级:P）

最后回归开篇第一个问题，你觉得这种配置有问题么？

参考：

1\. 扩展巴科斯范式（EBNF）： [Extended Backus-Naur Form](http://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form) ， [Backus-Naur Form](http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form) , [巴科斯范式](http://zh.wikipedia.org/wiki/%E5%B7%B4%E7%A7%91%E6%96%AF%E8%8C%83%E5%BC%8F) ， [扩展巴科斯范式](http://zh.wikipedia.org/wiki/%E6%89%A9%E5%B1%95%E5%B7%B4%E7%A7%91%E6%96%AF%E8%8C%83%E5%BC%8F)

2\. sudoers： [Sudoers Manual](http://www.gratisoft.us/sudo/sudoers.man.html)

3\. Sudoers的Default可配置参数请参看2中Sudoers Manual中的SUDOERS OPTIONS

4.其他一些参考： [Archlinux SUDO](https://wiki.archlinux.org/index.php/Sudo_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) , [Ubuntu Sudoers](https://help.ubuntu.com/community/Sudoers) , [sudo.ws SUDO manual](http://www.sudo.ws/sudo/sudo.man.html)
