gethostname函数在C中为什么会导致PATH环境变量被清除?

huangapple go评论77阅读模式
英文:

Why gethostname function in C causes the PATH env to be cleared?

问题

这段代码是一个名为imcsh的小型shell程序。在运行ls命令时显示"command not found"错误。但是当注释掉获取主机名的部分后,ls命令可以正常运行。您尝试使用setenv("PATH", "/bin:/usr/bin:/usr/local/bin", 1);重新设置了PATH变量,但问题仍然存在。您想知道问题出在哪里。

英文:

This is a code for a small shell called imcsh:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

#define MAX_INPUT_SIZE 1024
#define MAX_TOKEN_SIZE 64
#define MAX_NUM_TOKENS 64

// Tokenizes the input string based on whitespace
void tokenizeInput(char *input, char **tokens, int *numTokens)
{
    char *token = strtok(input, " \t\n");

    *numTokens = 0;
    while (token != NULL && *numTokens < MAX_NUM_TOKENS)
    {
        tokens[*numTokens] = token;
        (*numTokens)++;
        token = strtok(NULL, " \t\n");
    }
}

// Executes the given command and waits for it to finish
void executeCommand(char **tokens, int numTokens, int background)
{
    pid_t pid = fork();

    if (pid == 0)
    { // Child process
        if (background)
        {
            // Ignore SIGINT signal for background processes
            signal(SIGINT, SIG_IGN);
        }

        // Execute the command
        execvp(tokens[0], tokens);

        // If execvp returns, there was an error
        printf("imcsh: command not found: %s\n", tokens[0]);
        exit(1);
    }
    else if (pid > 0)
    { // Parent process
        if (!background)
        {
            // Wait for the child process to finish
            waitpid(pid, NULL, 0);
        }
        else
        {
            // Print the PID of the background process
            printf("Background process PID: %d\n", pid);
        }
    }
    else
    { // Error occurred
        printf("Failed to fork()\n");
        exit(1);
    }
}

// Prints the version of imcsh
void showVersion()
{
    printf("IMCSH Version 1.1 created by me.\n");
}

// Redirects the output to a file
void redirectOutput(char **tokens, int numTokens)
{
    // Find the ">" symbol in the tokens
    int i;
    for (i = 0; i < numTokens; i++)
    {
        if (strcmp(tokens[i], ">") == 0)
        {
            break;
        }
    }

    // If ">" symbol found and there is a file name after it
    if (i < numTokens - 1)
    {
        char *filename = tokens[i + 1];

        // Open the file for writing (create if it doesn't exist, append if it does)
        FILE *file = fopen(filename, "a");
        if (file != NULL)
        {
            // Duplicate the file descriptor of the opened file
            int fd = fileno(file);

            // Redirect the stdout to the file descriptor
            dup2(fd, STDOUT_FILENO);

            fclose(file);
        }
        else
        {
            printf("imcsh: failed to open file: %s\n", filename);
        }
    }
    else
    {
        printf("imcsh: invalid command syntax\n");
    }
}

int main()
{
    char input[MAX_INPUT_SIZE];
    char *tokens[MAX_NUM_TOKENS];
    int numTokens;
    char *username = getlogin();
    if (username == NULL)
    {
        printf("Failed to get username.\n");
    }

    char hostname[1024];
    if (gethostname(hostname, sizeof(hostname)) != 0)
    {
        printf("Failed to get hostname.\n");
        exit(1);
    }

    while (1)
    {
        // Print the command prompt
        // printf("%s@%s> ", username, hostname);
        printf("user@host> ");

        // Read user input
        fgets(input, sizeof(input), stdin);
        // Tokenize the input
        tokenizeInput(input, tokens, &numTokens);

        if (numTokens > 0)
        {
            // Check for internal commands
            if (strcmp(tokens[0], "exec") == 0)
            {
                int background = 0;

                // Check for "&" symbol at the end (for background execution)
                if (numTokens > 1 && strcmp(tokens[numTokens - 1], "&") == 0)
                {
                    background = 1;
                    tokens[numTokens - 1] = NULL; // Remove "&" symbol
                }

                // Execute the command
                executeCommand(tokens + 1, numTokens - 1, background);
            }
            else if (strcmp(tokens[0], "globalusage") == 0)
            {
                showVersion();
            }
            else if (strcmp(tokens[0], "quit") == 0)
            {
                // Check for running processes
                int status;
                pid_t result;

                do
                {
                    result = waitpid(-1, &status, WNOHANG);
                    if (result > 0)
                    {
                        printf("Running process: %d\n", result);
                    }
                } while (result > 0);

                // Prompt for confirmation
                printf("The following processes are running, are you sure you want to quit? [Y/n]: ");

                char confirm[5];
                fgets(confirm, sizeof(confirm), stdin);

                if (confirm[0] == 'Y' || confirm[0] == 'y')
                {
                    // Terminate all running processes and exit the shell
                    kill(0, SIGTERM);
                    break;
                }
            }
            else
            {
                // External command, execute it
                executeCommand(tokens, numTokens, 0);
            }
        }
    }

    return 0;
}

if I run this and enter a command, for example, ls, it says command not found. But if I comment this section:

if (gethostname(hostname, sizeof(hostname)) != 0)
    {
        printf("Failed to get hostname.\n");
        exit(1);
    } 

ls will work. I tried to set PATH variable again using setenv("PATH", "/bin:/usr/bin:/usr/local/bin", 1);
But the problem still persists. What is the problem?

答案1

得分: 3

你没有正确构建令牌列表。

execv 函数的第二个参数是一个字符串数组,预期应以 NULL 指针终止。你没有设置这个空指针,所以 execv 会继续读取参数,直到找到一个 NULL。这将导致读取未初始化的数组元素,可能超出数组的末尾,触发未定义的行为。

在填充令牌列表后,需要在末尾添加一个 NULL 指针:

void tokenizeInput(char *input, char **tokens, int *numTokens)
{
    char *token = strtok(input, " \t\n");

    *numTokens = 0;
    while (token != NULL && *numTokens < MAX_NUM_TOKENS - 1)
    {
        tokens[*numTokens] = token;
        (*numTokens)++;
        token = strtok(NULL, " \t\n");
    }
    tokens[*numTokens] = NULL;
}
英文:

You're not constructing the token list correctly.

The second argument to the execv function is an array of strings which is expected to be terminated by a NULL pointer. You don't set this null pointer, to execv keeps reading arguments until it finds one. This will read into uninitialized array elements and possibly past the end of the array, triggering undefined behavior.

After you've populated the token list, add a NULL pointer to the end:

void tokenizeInput(char *input, char **tokens, int *numTokens)
{
    char *token = strtok(input, &quot; \t\n&quot;);

    *numTokens = 0;
    while (token != NULL &amp;&amp; *numTokens &lt; MAX_NUM_TOKENS - 1)
    {
        tokens[*numTokens] = token;
        (*numTokens)++;
        token = strtok(NULL, &quot; \t\n&quot;);
    }
    tokens[*numTokens] = NULL;
}

huangapple
  • 本文由 发表于 2023年6月29日 22:53:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76582222.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定