关于unix高级环境编程 编译时的err_sys和err_quit错误

本文介绍了一组用于处理系统调用错误的自定义C语言函数,包括非致命和致命错误处理,并提供了完整的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

err_sys以及err_quit等函数不是C语言自带函数,是作者自己编写的函数。所以,想要运行书中的源代码,就必须自建一个头文件my_err.h把作者的代码拷贝进去,然后在程序中加载。



  1. #include <errno.h> /* for definition of errno */
    #include <stdarg.h> /* ISO C variable aruments */
    
    static void err_doit(int, int, const char *, va_list);
    
    /*
     * Nonfatal error related to a system call.
     * Print a message and return.
     */
    void
    err_ret(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(1, errno, fmt, ap);
        va_end(ap);
    }
    
    
    /*
     * Fatal error related to a system call.
     * Print a message and terminate.
     */
    void
    err_sys(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(1, errno, fmt, ap);
        va_end(ap);
        exit(1);
    }
    
    
    /*
     * Fatal error unrelated to a system call.
     * Error code passed as explict parameter.
     * Print a message and terminate.
     */
    void
    err_exit(int error, const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(1, error, fmt, ap);
        va_end(ap);
        exit(1);
    }
    
    
    /*
     * Fatal error related to a system call.
     * Print a message, dump core, and terminate.
     */
    void
    err_dump(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(1, errno, fmt, ap);
        va_end(ap);
        abort(); /* dump core and terminate */
        exit(1); /* shouldn't get here */
    }
    
    
    /*
     * Nonfatal error unrelated to a system call.
     * Print a message and return.
     */
    void
    err_msg(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(0, 0, fmt, ap);
        va_end(ap);
    }
    
    
    /*
     * Fatal error unrelated to a system call.
     * Print a message and terminate.
     */
    void
    err_quit(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(0, 0, fmt, ap);
        va_end(ap);
        exit(1);
    }
    
    
    /*
     * Print a message and return to caller.
     * Caller specifies "errnoflag".
     */
    static void
    err_doit(int errnoflag, int error, const char *fmt, va_list ap)
    {
        char buf[MAXLINE];
       vsnprintf(buf, MAXLINE, fmt, ap);
       if (errnoflag)
           snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s",
             strerror(error));
       strcat(buf, "\n");
       fflush(stdout); /* in case stdout and stderr are the same */
       fputs(buf, stderr);
       fflush(NULL); /* flushes all stdio output streams */
    }


<think>根据用户需求,需要在国产操作系统中使用C语言编程实现以sudo权限运行Shell脚本,并通过弹窗显示执行进度结果。我们可以分解为以下几个步骤: 1. 使用C语言调用系统命令执行Shell脚本(需要sudo权限) 2. 在C程序中捕获Shell脚本执行的输出(包括标准输出错误输出) 3. 使用图形界面库(如GTK+或Qt)创建弹窗显示执行进度结果 考虑到国产操作系统(如统信UOS、麒麟等)通常基于Linux,且支持常见的图形界面库,这里我们选择GTK+作为图形库示例,因为它轻量且广泛使用。 步骤概述: - 使用`popen`或`popen2`等函数执行带sudo的shell命令,并读取输出。 - 使用GTK+创建弹窗,并在弹窗中显示执行进度(实时输出)最终结果。 注意:由于sudo需要密码,我们可以通过以下方式之一处理: 1. 在C程序中通过图形界面弹出密码输入框(如使用GTK的输入对话框),然后传递给sudo。 2. 配置免密码sudo(安全性需注意,仅适用于特定命令)。 这里我们采用第一种方法,即弹出一个密码输入对话框。 具体实现思路: 1. 创建GTK窗口,包含一个按钮用于触发执行。 2. 点击按钮后,弹出密码输入对话框。 3. 使用输入的密码,通过`echo <password> | sudo -S <command>`的方式执行命令(-S选项使sudo从标准输入读取密码)。 4. 在执行过程中,实时读取命令输出并显示在文本视图(GtkTextView)中。 5. 命令执行完毕后,显示最终结果(成功或失败)。 由于涉及图形界面异步执行(避免界面卡死),我们需要使用多线程或GLib的异步函数。这里使用GLib的`g_spawn_async_with_pipes`GIOChannel来异步读取输出。 代码结构: - 创建主窗口,包含一个“执行”按钮一个文本视图用于显示输出。 - 点击按钮后,弹出密码输入对话框。 - 获取密码后,在子进程中执行sudo命令,并将输出重定向到管道。 - 在主线程中监听管道,实时更新文本视图。 注意:在国产操作系统中,可能需要安装相应的开发库(如gtk+-3.0)。 示例代码(使用GTK3): ```c #include <gtk/gtk.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> // 全局组件 GtkWidget *text_view; GtkTextBuffer *buffer; GtkWidget *window; // 用于存储密码命令 gchar *password = NULL; gchar *command = "bash /path/to/your/script.sh"; // 替换为你的脚本路径 // 更新文本视图的回调函数(线程安全) void append_text(const gchar *text) { GtkTextIter end; gtk_text_buffer_get_end_iter(buffer, &end); gtk_text_buffer_insert(buffer, &end, text, -1); } // 从管道读取输出并更新UI gboolean read_output(GIOChannel *channel, GIOCondition condition, gpointer data) { gchar buf[1024]; gsize len; if (g_io_channel_read_chars(channel, buf, sizeof(buf) - 1, &len, NULL) == G_IO_STATUS_NORMAL) { buf[len] = '\0'; // 使用idle_add确保在主线程更新UI g_idle_add((GSourceFunc)append_text, g_strdup(buf)); } return TRUE; } // 执行命令的函数 void execute_command() { // 构建完整的命令:echo <password> | sudo -S <command> gchar *full_command = g_strdup_printf("echo %s | sudo -S %s", password, command); g_free(password); // 使用后立即清除密码 // 设置管道 gint out_fd, err_fd; GIOChannel *out_channel, *err_channel; gchar *argv[] = {"/bin/sh", "-c", full_command, NULL}; // 异步执行命令 GPid pid; g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, NULL, &out_fd, &err_fd, NULL); // 创建通道并监听标准输出错误输出 out_channel = g_io_channel_unix_new(out_fd); g_io_channel_set_encoding(out_channel, NULL, NULL); g_io_add_watch(out_channel, G_IO_IN, read_output, NULL); err_channel = g_io_channel_unix_new(err_fd); g_io_channel_set_encoding(err_channel, NULL, NULL); g_io_add_watch(err_channel, G_IO_IN, read_output, NULL); // 等待子进程结束(在后台进行,避免阻塞UI) g_child_watch_add(pid, (GChildWatchFunc) [](GPid pid, gint status, gpointer data) { waitpid(pid, NULL, 0); // 回收子进程 append_text("\n执行完成!\n"); }, NULL); g_free(full_command); } // 密码输入对话框的回调 void on_dialog_response(GtkDialog *dialog, gint response_id, gpointer user_data) { if (response_id == GTK_RESPONSE_OK) { GtkEntry *entry = GTK_ENTRY(user_data); password = g_strdup(gtk_entry_get_text(entry)); // 关闭对话框 gtk_widget_destroy(GTK_WIDGET(dialog)); // 执行命令 execute_command(); } else { gtk_widget_destroy(GTK_WIDGET(dialog)); } } // 按钮点击回调:弹出密码输入对话框 void on_button_clicked(GtkWidget *widget, gpointer data) { GtkWidget *dialog, *content_area, *entry; dialog = gtk_dialog_new_with_buttons("输入密码", GTK_WINDOW(window), GTK_DIALOG_MODAL, "确定", GTK_RESPONSE_OK, "取消", GTK_RESPONSE_CANCEL, NULL); content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); // 密码模式 gtk_container_add(GTK_CONTAINER(content_area), entry); gtk_widget_show_all(dialog); g_signal_connect(dialog, "response", G_CALLBACK(on_dialog_response), entry); } int main(int argc, char *argv[]) { gtk_init(&argc, &argv); // 创建主窗口 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "执行Shell脚本"); gtk_window_set_default_size(GTK_WINDOW(window), 600, 400); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); // 创建垂直布局 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); gtk_container_add(GTK_CONTAINER(window), vbox); // 创建按钮 GtkWidget *button = gtk_button_new_with_label("执行脚本"); g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), NULL); gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0); // 创建文本视图滚动窗口 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); text_view = gtk_text_view_new(); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)); gtk_container_add(GTK_CONTAINER(scrolled_window), text_view); gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0); gtk_widget_show_all(window); gtk_main(); return 0; } ``` 注意事项: 1. 此代码需要安装GTK开发库(在国产系统中通常可用包管理器安装)。 2. 编译命令:`gcc `pkg-config --cflags gtk+-3.0` -o sudo_executor sudo_executor.c `pkg-config --libs gtk+-3.0`` 3. 实际使用时,请将`command`变量替换为你的Shell脚本路径。 4. 密码通过明文传递存在安全风险,请根据实际需求评估。在生产环境中,建议使用其他安全机制(如Polkit)或配置免密码sudo(仅限特定命令)。 §§相关问题§§ 1. 在国产操作系统中如何安装GTK开发环境? 2. 如何配置免密码sudo执行特定命令? 3. 除了GTK,还有哪些适用于国产操作系统的图形界面库?</think>### 在国产系统中用C语言实现sudo执行Shell脚本并弹窗显示进度 #### 实现步骤 1. **执行sudo命令的核心方法** 使用`system()`函数执行`echo <密码> | sudo -S`命令,通过管道传递密码实现免交互: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { char cmd[256]; const char* password = "your_password"; // 实际应用中应从安全渠道获取 const char* script = "/path/to/script.sh"; // 构造sudo命令(-S表示从stdin读取密码) snprintf(cmd, sizeof(cmd), "echo %s | sudo -S %s", password, script); // 执行命令并捕获输出 FILE* fp = popen(cmd, "r"); if (fp) { char buffer[128]; while (fgets(buffer, sizeof(buffer), fp)) { // 此处处理实时输出(用于进度显示) printf("%s", buffer); } pclose(fp); } return 0; } ``` [^1][^2] 2. **弹窗显示进度(使用Zenity工具)** 国产系统(如统信UOS、麒麟)通常支持Zenity,可通过管道实时输出进度: ```c // 在while循环中添加进度推送 while (fgets(buffer, sizeof(buffer), fp)) { char zenity_cmd[256]; snprintf(zenity_cmd, sizeof(zenity_cmd), "zenity --progress --text='%s'", buffer); system(zenity_cmd); // 实时显示进度弹窗 } ``` 3. **完整流程整合** ```mermaid graph TD A[启动C程序] --> B[构造sudo命令] B --> C[通过popen执行命令] C --> D{是否读取到输出?} D -->|是| E[推送进度到Zenity弹窗] D -->|否| F[显示最终结果弹窗] E --> C ``` 4. **安全增强建议** - 密码存储:使用`libsecret`或系统密钥环服务 - 错误处理:检查sudo返回值 ```c int status = pclose(fp); if (WIFEXITED(status)) { int exit_code = WEXITSTATUS(status); if (exit_code == 0) { system("zenity --info --text='执行成功'"); } else { system("zenity --error --text='执行失败'"); } } ``` #### 注意事项 1. **权限配置** 推荐配置`/etc/sudoers`免密码执行特定脚本: ```bash # 在/etc/sudoers中添加 username ALL=(ALL) NOPASSWD: /path/to/script.sh ``` 这样可避免在代码中硬编码密码[^5]。 2. **国产系统适配** - 统信UOS:默认支持Zenity,需安装`libgtk-3-dev` - 麒麟系统:若缺少Zenity,可用`yad`替代(类似工具) 3. **替代方案** 如需原生GUI开发,可选用: - GTK3(跨平台支持) - Qt(提供`QProcess`类执行命令) - 深度DTK(统信UOS原生开发套件) > **提示**:实际部署时应移除代码中的明文密码,改用权限配置或安全输入对话框[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值