英文:
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, " \t\n");
*numTokens = 0;
while (token != NULL && *numTokens < MAX_NUM_TOKENS - 1)
{
tokens[*numTokens] = token;
(*numTokens)++;
token = strtok(NULL, " \t\n");
}
tokens[*numTokens] = NULL;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论