Linux C语言工具函数

文章目录

  • 1、概述
  • 2、计算文件md5值
  • 3、gmssl国密sm4加解密
  • 4、使用zlib库压缩解压
  • 5、获取毫秒时间
  • 6、ini类型配置文件读取
  • 7、守护脚本
  • 8、log日志使用
  • 9、拷贝脚本

1、概述

日常工作开发中遇到的问题,将每一个问题写成一个通用函数,方便后续开发复用

2、计算文件md5值

#include 
char hash_str[33] = {0};
int status = file_md5("tmp.txt",hash_str);

int file_md5(const char *file_name,uint8_t *mds)
{
    if(file_name == NULL ){
        LOG_ERROR(c,"Error:file_name is null");
        return -2;       
    }
    if(mds == NULL){
        LOG_ERROR(c,"Error:file_name is null");
        return -2;   
    }

    FILE *fp = fopen(file_name, "rb");
    if (!fp) {
        LOG_ERROR(c,"Error: Failed to open file");
        return -1;
    }

    uint8_t buf[BUFFER_SIZE];
    uint8_t md[16];
    MD5_CTX ctx;
    MD5_Init(&ctx);

    size_t n;
    while ((n = fread(buf, 1, BUFFER_SIZE, fp)) > 0) {
        MD5_Update(&ctx, buf, n);
    }

    MD5_Final(md, &ctx);
    fclose(fp);
    // for (int i = 0; i < 16; i++) {
    //     printf("%02x", md[i]);
    // }
    // printf("\n");

    size_t i, pos = 0;
    for (int i = 0; i < 16 && pos < 33 - 1; i++) 
    {
        pos += sprintf(mds + pos, "%02x", md[i]);
    }

    if (pos < 33) {
        mds[pos] = '\0';
    }
    return 0;
}

3、gmssl国密sm4加解密

加密

#include "gmssl/sm4.h"

/*
input_file:需要加密文件
output_file:加密后的文件
key:密钥
*/
int encrypt_file(const char *input_file, const char *output_file, const unsigned char *key)
{
    FILE *in, *out;
    SM4_KEY sm4_key;
    unsigned char buffer[16], out_buffer[16];
    size_t read_len;

    //printf("input_file:%s\n",input_file);
    if ((in = fopen(input_file, "rb")) == NULL) {
        printf("Cannot open input file: %s\n", input_file);
        return 1;
    }

    if ((out = fopen(output_file, "wb")) == NULL) {
        printf("Cannot open output file: %s\n", output_file);
        fclose(in);
        return 1;
    }

    sm4_set_encrypt_key(&sm4_key, key);
    unsigned char size_data;
    while ((read_len = fread(buffer, 1, sizeof(buffer), in)) > 0) {
        if (read_len < sizeof(buffer)) 
        {
            memset(buffer + read_len, 0, sizeof(buffer) - read_len);
            sm4_encrypt(&sm4_key, buffer, out_buffer);
            fwrite(out_buffer, 1, sizeof(out_buffer), out);
            // 记录插入值
            size_data = (sizeof(buffer) - read_len) & 0xff;
            fwrite(&size_data,1,1,out);
        }
        else
        {
            sm4_encrypt(&sm4_key, buffer, out_buffer);
            fwrite(out_buffer, 1, sizeof(out_buffer), out);   
        }
    }
    fclose(in);
    fclose(out);
    return 0;
}

解密:

/*
input_file:需要解密文件
output_file:解密后的文件
key:密钥
*/
int decrypt_file(const char *input_file, const char *output_file, const unsigned char *key) 
{
    m_assert(input_file,"input_file is null",-1);
    m_assert(input_file,"output_file is null",-1);
    m_assert(key,"key is null",-1);
    
    FILE *in, *out;
    SM4_KEY sm4_key;
    unsigned char buffer[16], out_buffer[16];
    size_t read_len;
    uint8_t last_data[BLOCK_SIZE];
    size_t last_data_len = 0;
    size_t data_len = 0;

    if ((in = fopen(input_file, "rb")) == NULL) {
        LOG_ERROR(c,"Cannot open input file: %s", input_file);
        return -1;
    }

    if ((out = fopen(output_file, "wb")) == NULL) {
        LOG_ERROR(c,"Cannot open output file: %s", output_file);
        fclose(in);
        return -1;
    }
    sm4_set_decrypt_key(&sm4_key, key);
    
    // 获取文件大小
    fseek(in,0,SEEK_END);
    data_len = ftell(in);

    char size_data;
    int data_size;
    if(data_len % 16)
    {
        
        fseek(in, -1, SEEK_END);
        fread(&size_data, 1, 1, in);
        data_size = size_data;
    }
    fseek(in, 0, SEEK_SET);
    data_len -= 1;
    while ((read_len = fread(buffer, 1, sizeof(buffer), in)) > 0) 
    {
        if(data_len == 16)
        {
            sm4_decrypt(&sm4_key, buffer, out_buffer);
            fwrite(out_buffer, 1, sizeof(out_buffer)-data_size, out);
            break;
        }
        else
        {
            sm4_decrypt(&sm4_key, buffer, out_buffer);
            fwrite(out_buffer, 1, sizeof(out_buffer), out);
        }
        data_len -= 16;
    }
    fclose(in);
    fclose(out);
    return 0;
}

编译

GMSSL = -lgmssl -lcrypto
GMSSL_LIB = -L/usr/local/gmssl/lib
GMSSL_INCLUDE = -I/usr/local/gmssl/include

4、使用zlib库压缩解压

压缩

#include 
/*
output_filename:压缩包名
input_filenames:需要压缩文件的结构体数组
num_files:有几个文件
*/

typedef struct _encry_file
{
    char server_path[MAX_LINE_LENGTH];
    char server_encry_file[MAX_LINE_LENGTH];
    int status;
}encry_file;

int compress_files(const char* output_filename, encry_file** input_filenames, int num_files)
{
    gzFile fout = gzopen(output_filename, "wb");
    if (fout == NULL) {
        printf("Failed to create output file: %s\n", strerror(errno));
        return -1;
    }

    int ret = Z_OK;
    uint8_t buf[BUFFER_SIZE];
    int i;
    for (i = 0; i < num_files; i++) 
    {
        //printf("%d\n",num_files);
        FILE* fin = fopen(input_filenames[i]->server_encry_file, "rb");
        if (fin == NULL) {
            printf("Failed to open input file: %s\n", strerror(errno));
            gzclose(fout);
            return -1;
        }

        // 写入文件名
        struct stat st;
        if (stat(input_filenames[i]->server_encry_file, &st) != 0)
        {
            printf("Failed to write filename to output file: %s\n", gzerror(fout, &ret));
            gzclose(fout);
            fclose(fin);
            return -1;
        }

        printf("File size: %s\t%ld bytes\n",input_filenames[i]->server_encry_file, st.st_size);
        ret = gzprintf(fout, "%s^%d\n", input_filenames[i]->server_encry_file,st.st_size);
        if (ret <= 0) {
            printf("Failed to write filename to output file: %s\n", gzerror(fout, &ret));
            gzclose(fout);
            fclose(fin);
            return -1;
        }

        // 压缩文件内容
        int flush = Z_NO_FLUSH;
        size_t size_read = 0;
        while ((size_read = fread(buf, 1, BUFFER_SIZE, fin)) > 0) {
            ret = gzwrite(fout, buf, size_read);
            if (ret < 0) {
                printf("Failed to write to output file: %s\n", gzerror(fout, &ret));
                gzclose(fout);
                fclose(fin);
                return -1;
            }
        }

        fclose(fin);
    }

    gzclose(fout);
    return 0;
}

解压:

int decompress_file(const char* input_filename)
{
    m_assert(input_filename,"input_filename is null",-1);
    gzFile fin = gzopen(input_filename, "rb");
    if (fin == NULL) {
        LOG_ERROR(c,"Failed to open input file: %s\n", strerror(errno));
        return -1;
    }

    int ret = Z_OK;
    uint8_t buf[BUFFER_SIZE];
    while (1) 
    {
        char filename_inzip[256] = {0};
        int file_size = 0;
        if (gzgets(fin, filename_inzip, sizeof(filename_inzip)) == NULL) {
            if (gzeof(fin)) {
                break;
            } else {
                LOG_ERROR(c,"Failed to read filename from input file: %s\n", gzerror(fin, &ret));
                gzclose(fin);
                return -1;
            }
        }

        char* p = strchr(filename_inzip, '\n');
        if (p != NULL) {
            *p = '\0';
        }
        char *p1 = strchr(filename_inzip,'^');
        if(p1 != NULL){
            *p1 = '\0';
        }
        file_size = atoi(p1+1);
        
        FILE* fout = fopen(filename_inzip, "wb");
        if (fout == NULL) {
            LOG_ERROR(c,"Failed to create output file: %s\n", strerror(errno));
            gzclose(fin);
            return -1;
        }

        size_t size_read = 0;
        if(file_size < BUFFER_SIZE)
        {
            size_read = gzread(fin, buf, file_size);
            if (fwrite(buf, 1, size_read, fout) != size_read) {
                LOG_ERROR(c,"Failed to write to output file: %s\n", strerror(errno));
                fclose(fout);
                gzclose(fin);
                return -1;
            }
            fclose(fout);
            continue;
        }

        size_t size_write = BUFFER_SIZE;
        while (file_size!=0)
        {
            if(file_size < BUFFER_SIZE)
            {
                size_write = file_size;
            }

            if((size_read = gzread(fin, buf, size_write)) > 0)
            {
                if (fwrite(buf, 1, size_read, fout) != size_read) {
                    LOG_ERROR(c,"Failed to write to output file: %s\n", strerror(errno));
                    fclose(fout);
                    gzclose(fin);
                    return -1;
                }
            }
            file_size -= size_write;
        }

        fclose(fout);
    }

    gzclose(fin);
    return 0;
}

编译:

-lz

5、获取毫秒时间

unsigned long long get_milliseconds()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (unsigned long long)(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
}

6、ini类型配置文件读取

typedef struct _CConfItem
{
	char ItemName[50];
	char ItemContent[500];
}CConfItem;

int load_config(const char *path_name)
{    
    FILE *fp;
    fp = fopen(path_name,"r");
    if(fp == NULL)
        return -1;

    char  linebuf[501];
    while(!feof(fp))
    {    
        if(fgets(linebuf,500,fp) == NULL)
            continue;
        if(linebuf[0] == 0)
            continue;
        if(*linebuf==';' || *linebuf==' ' || *linebuf=='#' || *linebuf=='\t'|| *linebuf=='\n')
			continue;
        
    lblprocstring:
		if(strlen(linebuf) > 0)
		{
			if(linebuf[strlen(linebuf)-1] == 10 || linebuf[strlen(linebuf)-1] == 13 || linebuf[strlen(linebuf)-1] == 32) 
			{
				linebuf[strlen(linebuf)-1] = 0;
				goto lblprocstring;
			}		
		}
        if(linebuf[0] == 0)
            continue;
        if(*linebuf=='[')
			continue;

        char *ptmp = strchr(linebuf,'=');
        if(ptmp != NULL)
        {
            if(i_conf_row < MAX_ROWS)
            {
                strncpy(config_items[i_conf_row]->ItemName,linebuf,(int)(ptmp-linebuf)); 
                strcpy(config_items[i_conf_row]->ItemContent,ptmp+1);

                Rtrim(config_items[i_conf_row]->ItemName);
                Ltrim(config_items[i_conf_row]->ItemName);
                Rtrim(config_items[i_conf_row]->ItemContent);
                Ltrim(config_items[i_conf_row]->ItemContent);
                config_items[i_conf_row] = config_items[i_conf_row];
                i_conf_row++;
            }
            else
            {
                fclose(fp);
                return 0;
            }
        }
    }

    fclose(fp);
    return 0;
}

去除左右空格

void Rtrim(char *string)   
{   
	size_t len = 0;   
	if(string == NULL)   
		return;   

	len = strlen(string);   
	while(len > 0 && string[len-1] == ' ')   
		string[--len] = 0;   
	return;   
}

void Ltrim(char *string)
{
	size_t len = 0;
	len = strlen(string);   
	char *p_tmp = string;
	if( (*p_tmp) != ' ')
		return;
	while((*p_tmp) != '\0')
	{
		if( (*p_tmp) == ' ')
			p_tmp++;
		else
			break;
	}
	if((*p_tmp) == '\0')
	{
		*string = '\0';
		return;
	}
	char *p_tmp2 = string; 
	while((*p_tmp) != '\0')
	{
		(*p_tmp2) = (*p_tmp);
		p_tmp++;
		p_tmp2++;
	}
	(*p_tmp2) = '\0';
    return;
}

通过name获取value

const char *getstring(const char *p_itemname)
{
    for(int i = 0; i<i_conf_row; ++i)
	{	
		if(strcasecmp(config_items[i]->ItemName,p_itemname) == 0)
			return config_items[i]->ItemContent;
	}
	return NULL;
}

int getintDefault(const char *p_itemname,const int def)
{
    for(int i = 0; i<i_conf_row; ++i)
	{	
		if(strcasecmp(config_items[i]->ItemName,p_itemname) == 0)
			return atoi(config_items[i]->ItemContent);
	}
	return def;
}

7、守护脚本

#!/bin/sh
var_name="MASTER_UPD_REQ"
var_value="0"
master_upd_name="/tmp/master_upd.ini"

# 要监控的C语言程序名称
program_name="terminal_pro"
program_name_path="/home/upd_framework"

# 用于存储程序运行状态的文件
status_file="/tmp/program_status.txt"

# 用于记录程序是否正常运行的变量
is_running=0

function set_param
{
    # 返回升级结果
    gflag=$1
    if [ ! -f "$master_upd_name" ]; then
        echo "文件 $master_upd_name 不存在"
        return 2
    fi
    # 备份文件
    cp "$master_upd_name" "${master_upd_name}.bak"

    # 修改配置项
    sed -i "s/^MASTER_UPD_STATUS=0$/MASTER_UPD_STATUS=$gflag/" "${master_upd_name}.bak"
    mv "${master_upd_name}.bak" $master_upd_name

    if [ $? -ne 0 ]; then
        return 3
    fi
    return 0    
}


# 检查程序是否在运行
check_program_running() {
    #pgrep -x "$program_name" > /dev/null
    if ps aux | grep "$program_name" | grep -v grep > /dev/null ; then
        is_running=1
    else
        #echo "程序 $program_name 没有运行。"
        is_running=0
    fi
}

# 检查程序是否正常执行的函数
monitor_program() 
{
    if [ -f $master_upd_name ]
    then
        # 判断是否重启程序
        while true; do
            sleep 1
            #echo "正在主程序升级更新程序完成"
            var_line=$(grep -E "^$var_name[[:space:]]*=" "$master_upd_name")
            var_values=$(echo "$var_line" | cut -d= -f2 | tr -d '[:space:]')
            if [ "$var_value" = "$var_values" ]; then
                #echo "主程序更新程序完成"
                break
            fi
        done
        
    fi

    check_program_running
    if [ $is_running -eq 1 ]; then
        echo "The program $program_name is running normally." > $status_file
    else
        cd $program_name_path
        ./$program_name &
        check_program_running
        if [ $is_running -eq 1 ]; then
            if [ -f $master_upd_name ]
            then
                echo "重启新的主程序成功"
                set_param 1
            fi
        else
            if [ -f $master_upd_name ]
            then
                echo "重启新的主程序失败"
                set_param 0
            fi
        fi
    fi
}

# 每隔10秒检查一次程序状态
while true; do
    monitor_program
    sleep 10
done

8、log日志使用

将zlog的功能进行了裁剪,使用功能整合到了两个文件中

#include "log.h"

int main()
{
    int rc = log_init("main.conf");
    if (rc) {
        printf("init failed\n");
        return -1;
    }
    // 创建分类
    log_category_t *c = log_get_category("my_stdout");
    log_category_t *c1 = log_get_category("my_aa");
    log_category_t *c2 = log_get_category("my_time");
    log_category_t *c3 = log_get_category("my_cat");
    if (!c) {
        printf("get cat fail\n");
        log_fini();
        return -2;
    }

    // 按照my_stdout规则进行打印
    LOG_DEBUG(c,"hello world");
    LOG_INFO(c, "hello world");
    LOG_NOTICE(c, "hello world");
    LOG_WARN(c, "hello world");
    LOG_ERROR(c, "hello world");
    LOG_FATAL(c, "hello world");
    // 说明只能用来答应16进制字符串
    LOG_HEX(c,"71B");

    // 按照my_cat规则进行打印
    LOG_DEBUG(c3,"hello world");
    LOG_INFO(c3, "hello world");
    LOG_NOTICE(c3, "hello world");
    LOG_WARN(c3, "hello world");
    LOG_ERROR(c3, "hello world");
    LOG_FATAL(c3, "hello world");

    // 说明只能用来答应16进制字符串
    LOG_HEX(c3,"71B");
  
    // 释放内存
    log_fini();
    return 0;
}
[formats]
#   %d打日志的时间
#   %-8us 右补充空格,如果微秒小于8个字符长
#   %-5V 日志级别大写,右补充空格,如果日志级别小于5个字符长
#   %-8p 进程ID 右补充空格,如果进程ID小于8个字符长
#   %T 线程ID长整型
#   %F 源代码文件名
#   %L 源代码行数
#   %m 用户输出信息
#   %n 回车
simple	= "%d %-8us %-5V[%-6p %T %F %L] %m%n"

# 表示只输出用户输出信息和日志其他什么都不输出
c1 = "%m%n"

# 多打印一个日志等级 小于5个字符右边补空格
c2 = "%-5V %m%n"

[rules]
# my_stdout[分类名] info[类型日志]  >stdout[输出到控制台] ;simple[日志输出格式]
# my_stdout大于info类型的日志都会按照simple格式输出到控制台
my_stdout.debug   >stdout;simple

# my_aa分类大于info日志等级的输出到日志文件中
my_aa.info  "./aa.log";c1

my_time.* "./bb.log";c2

# my_cat下所有类型日志输出到 aa.加当前时间.log日志中
# 每个日志文件大小不能超过20MB,
# 超过创建新文件放在当前目录下aa.当前时间.log.(序号)
# 归档文件保存最多5个
# #s序号会混动一直递增(归档文件后面的序号)
# #r序号不会动
my_cat.*        "aa.%d(%F).log",20MB*5~"aa.%d(%F).#r.log";simple

my_thread.* "./aa.log";simple

9、拷贝脚本

#!/bin/bash
args=("$@")
zip_name="${args[4]}.bak"

# 定义一个空数组来保存所需信息
declare -a local_oper_type
declare -a loacl_path
declare -a terminal_path

del_dir="del"
exec_file="exec"

config_file_name="pack.ini"
root_dir="/root/test-2023-05-08/ini_parse/pack_c++/"
terminal_dir="/root/test-2023-05-08/ini_parse/pack_c++/sh_test"
make_="/root/build.sh"
make_pack="/root/build.sh pack"

function read_config_file 
{
    read_flag=0

    while IFS= read -r line || [[ -n "$line" ]]; do
        if [[ $line == *"[loacl_path]"* ]]; then
            read_flag=1
        elif [[ $line == *"[terminal_path]"* ]]; then
            read_flag=2
        fi

        if [[ $line != *"="* ]]; then
            continue
        fi

        if [ $read_flag -eq 1 ]; then
            if [[ ${line:0:1} != "#" ]]; then
                key=$(echo $line | awk -F '=' '{print $1}' | tr -d ' ')
                local_oper_type+=("$key")
            else
                value=$(echo $line | cut -d'=' -f2 | tr -d ' ')
                loacl_path+=("$root_dir$value")
            fi
        elif [ $read_flag -eq 2 ]; then
            terminal_value=$(echo $line | cut -d'=' -f2 | tr -d ' ')
            terminal_path+=("$terminal_dir$terminal_value")
        fi
    done < $config_file_name
    return 0
}

function copy_upd_pack
{
    # 检查两个数组的长度是否相同
    if [ ${#loacl_path[@]} -ne ${#terminal_path[@]} ]; then
        return 7  
    fi
    # 循环拷贝数组1中的数据到数组2的目录下
    for ((i=0; i<${#loacl_path[@]}; i++)); do
        if [ "${local_oper_type[i]}" == "$del_dir" ]; then
            rm -rf "${terminal_path[i]}"
            continue
        fi

        prefix=$(echo "${terminal_path[i]}" | sed "s|/[^/]*$||")
        if [ ! -d "$prefix" ]
        then
            mkdir -p "$prefix"
        fi
        
        # 拷贝到临时目录下
        cp "${loacl_path[i]}" "${terminal_path[i]}.tmp"
        if [ $? -ne 0 ]; then
            echo "拷贝失败:${loacl_path[i]}  ${terminal_path[i]}.tmp"
            return 8
        fi
    done

    # 移动目录 原子操作
    for ((i=0; i<${#loacl_path[@]}; i++)); do
        if [ "${local_oper_type[i]}" == "$del_dir" ]; then
            continue
        fi
        
        mv "${terminal_path[i]}.tmp" "${terminal_path[i]}"
        if [ $? -ne 0 ]; then
            echo "移动失败:${terminal_path[i]}"
            return 9
        fi
        
        if [ "${local_oper_type[i]}" == "$exec_file" ]; then
            chmod a+x "${terminal_path[i]}"
        fi
    done
    return 0
}

read_config_file
if [ $? -ne 0 ]; then
    echo "读取配置文件失败"
    exit 1
fi

copy_upd_pack
if [ $? -ne 0 ]; then
    echo "拷贝出错了"
    exit 2
fi

# 执行./build
# $make_
# $make_pack

echo "local_oper_type: ${local_oper_type[@]}"
echo "loacl_path: ${loacl_path[@]}"
echo "terminal_path: ${terminal_path[@]}"
[loacl_path]
del = #1
add = #2
add = #3
exec = #4

#1 =  1.log
#2 =  yaml
#3 =  yaml.c
#4 = main

[terminal_path]
#1 = /1.log
#2 = /test-2/yaml
#3 = /test-3/yaml.c
#4 = /main

你可能感兴趣的:(linux,c语言,c++)