一、关于glog库

golang中的glog库是google著名开源C++日志库glog的golang版本,在golang默认日志库的基础上做了更进一层的封装使得该库能更贴近日常使用。项目首页为golang/glog,当前版本的glog有以下几个特点:

  1. 支持四种日志级别:INFO < WARNING < ERROR < FATAL,支持不同级别的日志打印到不同文件中。
  2. 默认情况下日志不是打印到标准输出或标准错误,需要手动添加参数才能把日志打到标准错误中。
  3. 支持根据文件大小来切割文件,但是不支持根据日期切割。
  4. 日志输出的格式固定且不可修改:Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg,其中L表示日志级别首字符,如Info日志,L打印出来是ID`。
  5. 源码实现简单,方便自主修改。

安装glog:

go get github.com/golang/glog

1.1 基本用法

glog依赖命令行参数输入,默认是打印到文件的,在使用该库前必须先使用flag包解析命令行参数:

flag.Parse()

因为glog底层实现是先把日志输入到缓冲区buffer中,然后定时写入文件,所以在执行完日志输出后要手动执行glog.Flush()确保日志刷新到文件。一般在函数开始处执行:

defer glog.Flush()

输出日志时,需要加上-log_dir参数以指定目录写入日志文件,如以下代码:

package main

import (
    "flag"
    "github.com/golang/glog"
)

func main() {
    flag.Parse()
    defer glog.Flush()

    glog.Info("HelloWorld")
}

编译后执行:

./glog -log_dir=log

在log目录下会生成相应的日志文件,查看对应的日志:

打印到标准输出

默认情况下,日志不是打印到标准输出中,如需打印到标准输出可以使用以下两个参数:

  1. logtostderr:打印到标准错误而不是文件。
  2. alsologtostderr:同时打印到标准错误。

这两个参数都会把日志打印到标准错误中(在linux终端环境下,前台显示的标准错误和标准输出可以认为是同一个输出),调试日志时可以加上这两个参数中的任一:

二、日志级别

v levelv module功能是最常用到的功能,适用于给同一套代码在不同环境下设置不同日志级别的功能。如在调试环境下打印出更多级别的日志,生产环境下打印更少的日志。

2.1 打印特定级别的日志

使用方法:

glog.V(n).Info("Log message")

其中n表示的是日志级别,当n小于等于设定日志级别时将会被打印。默认情况下日志级别时0,启动时通过-v=x参数设定日志级别。如在代码中分别以级别3和级别5打印两条日志:

func main() {
    flag.Parse()
    defer glog.Flush()

    glog.V(3).Info("Level 3 log")
    glog.V(5).Info("Level 5 log")
}

执行时设定日志级别为4,日志将不会打印出第二条级别为5的日志:

./glog -v=4 -log_dir=log

日志:

Log file created at: 2019/09/08 18:04:55
Running on machine: maqianos
Binary: Built with gc go1.10.4 for linux/amd64
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
I0908 18:04:55.924127     546 main.go:12] Level 3 log

2.2 vmodule选项

vmodule选项可以在上面设定v level之后,单独再给某个文件设置日志级别,适用于以下场景:

  • 系统默认日志级别为3,但是希望main.go中的日志级别为5

执行:./glog -v=4 -log_dir=log -vmodule=main=5,日志中将会打印出级别为5的日志:

Log file created at: 2019/09/08 18:11:36
Running on machine: maqianos
Binary: Built with gc go1.10.4 for linux/amd64
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
I0908 18:11:36.096301     557 main.go:12] Level 3 log
I0908 18:11:36.100119     557 main.go:13] Level 5 log

三、自定义修改

3.1 修改日志WARN为DEBUG

默认是没有DEBUG日志的,WARN日志用得少可以把WARN日志改成DEBUG。在glog.go源码文件中,修改以下内容:

const severityChar = "IWEF" // 改为"IDEF"

var severityName = []string{
    infoLog:  "INFO",
    warningLog: "WARN" // 改为"DEBUG",
    errorLog: "ERROR",
    fatalLog: "FATAL",
}

这里实现简单,改起来也简单。

3.2 默认打印到标准错误

// 搜索函数init
func init() {
    // 注释掉下面两行
    //flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files")
    //flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files")
    
    // 添加下面两行
    logging.alsoToStderr = false
    logging.toStderr = true

    // Default stderrThreshold is ERROR.
    logging.stderrThreshold = errorLog

    logging.setVState(0, nil, false)
    go logging.flushDaemon()
}

一、strcpy

strcpy是字符串拷贝函数,将一个字符串拷贝到另一个字符串。

代码:

char *strcpy(char *dst, const char *src) { // [1]
    char *p = dst; // [2]

    if (src == NULL || dst == NULL) { // [3]
        return NULL;
    }

    while ((*dst++ = *src++) != '\0'); // [4]

    return p; // [5]
}

实现该函数的注意点:

  1. 传入参数src使用const修饰,避免函数内部修改数据。
  2. 使用p备份dst指针,在拷贝完成之后返回该值。
  3. 需要判断传入参数的合法性,避免出现不合理输入。
  4. 注意判断拷贝完成的标准是src[i] == '\0','\0'也要拷贝到dst字符串中。
  5. 返回拷贝完成的值,更好的支持链式表达式。

高级:

上面的实现是没有区分内存重叠的场景,假设源目地址存在重叠,使用上面的函数实现是有问题的。如图所示:

image.png

dst和src都是指向同一个数组,当把src的第三个元素赋值到dst后,src原有的'\0'被覆盖了,后续继续拷贝就出现问题。

解决这个问题其实很简单,只要从后往前复制src即可解决这个问题。可参考下面memcpy的实现。

二、memcpy

memcpy的作用是把一块内存区域拷贝到另外一个内存地址上面去。

实现:

void *memcpy(void *dst, const void *src, unsigned int count) { // [1]
    char *pdst, *psrc;

    if (dst == NULL || src == NULL || count == 0) { // [2]
        return NULL;
    }

    if (dst == src) { // [3]
        return dst;
    }

    if (dst > src) { // [4]
        pdst = (char *)dst + count - 1;
        psrc = (char *)src + count - 1;

        while (count--) {
            *pdst-- = *psrc--;
        }
    } else { // [5]
        pdst = (char *)dst;
        psrc = (char *)src;

        while (count--) {
            *pdst++ = *psrc++;
        }
    }

    return dst; // [6]
}

需要注意的点:

  1. src使用const修饰,count设置成无符号类型。
  2. 判断传入参数的有效性,避免无效输入。
  3. 判断源目地址相等的情况。
  4. 目的地址大于源地址,从高位向地位复制,避免出现内存重叠的问题。
  5. 目的地址小于源地址,从低位向高位复制。
  6. 返回复制好的目的地址指针以支持链式表达式。

三、memmove

memmove的作用是把一块内存空间的内容移动到另一个内存空间,实现方法和上面的memcpy一致。

其他参考:tcpdump的基本用法

1. 抓取指定网卡上的数据

tcpdump默认抓系统第一块网卡,-i参数可以指定网卡,any表示抓所有网卡:

tcpdump -i eth0 # 抓取eth0上的数据
tcpdump -i any # 抓取所有网卡上的数据

2. 抓取指定IP的数据

抓取来自eth0网卡上IP为192.168.10.1的数据:

tcpdump -i eth0 host 192.168.10.1

3. 抓取指定端口的数据

抓取HTTPS(443端口)的数据:

tcpdump -i eth0 tcp port 443

4. 抓取一个网段的数据

抓取192.168.10.0/24网段的数据:

tcpdump -i eth0 net 192.168.10.0/24

5. 多条件组合

tcpdump可以使用and/or/not来进行多条件组合抓包,当开启组合抓包时,建议使用双引号将语句包起来(否则当条件语句中存在括号等字符时会报错):

tcpdump -i eth0 "host 192.168.10.1 and (tcp port 443 or tcp port 80)" 

抓取来自192.168.10.1的443端口或80端口的数据。

6. 其他选项

  • -n:打印IP地址而不是主机名
  • -v:显示抓到的包的数量
  • -w file:将抓到的包写入文件
  • -c n:只抓取n个包,抓满后程序自动退出

把证书拷贝到:/usr/local/share/ca-certificates/

然后执行:

update-ca-certificates

注意事项

  1. curl命令不会使用这种机制,使用curl命令的时候要手动指定--insecure参数,否则依然会报证书不受信任的错误。
  2. wget不用单独再加参数。

一、问题现象

ubuntu上启动ssh时,报错:

 * Starting OpenBSD Secure Shell server sshd
Could not load host key: /etc/ssh/ssh_host_rsa_key
Could not load host key: /etc/ssh/ssh_host_ecdsa_key
Could not load host key: /etc/ssh/ssh_host_ed25519_key

截图信息:

image916886a7b54ba239.png

错误信息是说密钥不存在,查看目录确实是不存在:

root@maqianos:~# ll /etc/ssh/
total 552
drwxr-xr-x 1 root root   4096 May 21 22:41 ./
drwxr-xr-x 1 root root   4096 Sep 16 12:26 ../
-rw-r--r-- 1 root root 553122 Mar  4  2019 moduli
-rw-r--r-- 1 root root   1580 Mar  4  2019 ssh_config
-rw-r--r-- 1 root root    338 May 21 22:41 ssh_import_id
-rw-r--r-- 1 root root   3262 May 21 22:41 sshd_config

虽然有错误信息,但是从提示上看ssh服务是启动成功了,ps看进程也起来了。

不过客户端是不能远程上来的,连接时报错:

Connecting to 127.0.0.1:22...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
Connection closing...Socket close.

Connection closed by foreign host.

Disconnected from remote host(127.0.0.1:22) at 10:13:57.

同时使用wireshark抓包看,可以发现连接被服务端断开了:

二、解决方案

2.1 生成rsa_key

命令:

ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key

输出:

Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /etc/ssh/ssh_host_rsa_key.
Your public key has been saved in /etc/ssh/ssh_host_rsa_key.pub.
The key fingerprint is:
SHA256:HCF6EzKhpOZAk6vDO1wABnPVtckUYoIOazXqEc9SgfA root@maqianos
The key's randomart image is:
+---[RSA 2048]----+
|*o=+Bo=.=.       |
|+@o= *.* +       |
|==E o o =        |
|=B.o . o .       |
|=.+     S        |
|oo .             |
|..o              |
| +               |
|  .              |
+----[SHA256]-----+

2.2 生成ecdsa_key

使用命令:

ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key

输出:

Generating public/private ecdsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /etc/ssh/ssh_host_ecdsa_key.
Your public key has been saved in /etc/ssh/ssh_host_ecdsa_key.pub.
The key fingerprint is:
SHA256:XQ+3eH1AXF1TwFPH2/gll/CULXTmIpqHFdhLDvVGF5E root@maqianos
The key's randomart image is:
+---[ECDSA 256]---+
|           +o+=O^|
|          o o*=E*|
|           +=.X+=|
|         . *oBoO+|
|        S = o +++|
|           . .  o|
|                 |
|                 |
|                 |
+----[SHA256]-----+

2.3 生成ed25519_key

命令:

ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key

输出

Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /etc/ssh/ssh_host_ed25519_key.
Your public key has been saved in /etc/ssh/ssh_host_ed25519_key.pub.
The key fingerprint is:
SHA256:St4uTjtl3HIt9DYyZhao2KNRQQthguqE2WJLYhSBHpU root@maqianos
The key's randomart image is:
+--[ED25519 256]--+
|.+=.=o.          |
|oo E ...         |
|=o.   .. .       |
|**.   . . o      |
|B..  +.oSo +     |
| o  oo+o= X =    |
|     o++.* = .   |
|    ..oo         |
|     .oo.        |
+----[SHA256]-----+

2.3 重启ssh

执行/etc/init.d/sshd restart重启ssh服务,无报错信息:

imagec2f7ea1f2b5cee27.png

再次使用客户端连接可以连上!

linux环境配置新添加的网卡

linux设备在安装的时候会自动添加网卡,并初始化当前已有的网卡配置,安装完成后通过命令或者配置文件修改网络配置即可使用。但是当系统安装好后,再次添加网卡系统不会自动生成所需要的配置文件,使用ifconfig命令也不能直接看到网卡。要自己手动添加文件并修改配置才可以,流程繁琐,直接复制现有配置也容易配错。

这时候可以通过nmtui命令来配置,这个命令能自动识别出当前系统所有的的网卡,编辑后保存自动生成配置文件。

执行nmtui命令会进入到一个类似windows的图形化的界面:

选择Edit a connection进入到网卡配置页面:

选择要编辑的网卡后回车即可对指定网卡操作,可以修改网卡名字、IPv4/IPv6地址、修改DNS地址等等:

一、设置resolv.conf文件

配置的文件格式为:

nameserver 8.8.8.8 # 8.8.8.8是DNS服务器的地址

如:

二、修改网卡配置

在对应的网卡配置中加上DNS设置,网卡配置文件一般是cat /etc/sysconfig/network-scripts/ifcfg-网卡名,在里面加上:

DNS1=x.x.x.x
DNS2=x.x.x.x

三、修改hosts文件

和windows下一样,linux也提供了修改hosts文件可以固定死某个域名的IP地址:

静态路由表的配置文件为:/etc/sysconfig/network-scripts/route-网卡名

以网卡eth0为例,如果要针对eth0添加一条静态路由,可以在配置文件/etc/sysconfig/network-scripts/route-eth0中添加以下配置:

200.200.0.0/16 via 10.66.255.254
200.200.24.0/22 via 10.66.255.254

然后重启network服务即可生效!