Home > front end >  zsh illegal hardware instruction c
zsh illegal hardware instruction c

Time:12-07

I'm doing an exercise for the multiple execution of commands passed by the cmd arguments.

The description of the exercise is: "The program must read the file line by line, executing the command specified in the command line once for each line, replacing at each occurrence of '@' in the command line, the contents of the current line of the file."

But as soon as I execute ./file_exec esempio.txt cat @ the cli shows me this error zsh illegal hardware instruction.

I put here the various file that I've create:

main.c

#include "file_exec.h"

int main(int argc, char **argv) {
  if(argc < 3) {
    fprintf(stderr,
      "Specificare il file di testo e il comando da eseguire.\n");
    return 1;
  }

  char *filename = argv[1];
  int cmdargc = argc - 2;
  char **opz = argv   2;
  char *cmd = argv[2];
  char *line = (char *)malloc(sizeof(char) * BUZZ_SIZE);
  int statusLine = 0;
  FILE *file = fopen(filename, "r");

  if(file == NULL) {
    fprintf(stderr, "%s: Impossibile aprire %s: %s\n",
    argv[0], argv[1], strerror(errno));
    return 2;
  }
  int size = 1024;
  do{
    statusLine = fileReadLine(line, file, size); // leggo la singola linea
    if(statusLine == 2){
      size  = 1024;
    }
    if(strlen(line) != 0){
      int ris = executeLine(cmdargc, cmd, opz);

      if(ris == -1){
        fprintf(stderr, "Impossibile eseguire il comando: %s\n", cmd);
        return -1;
      }
    }

  }while(statusLine != -1);

  fclose(file);
  return 0;

}

file_exec.c

#include "file_exec.h"

int fileReadLine(char *line, FILE *file, int size){

  if(!feof(file) && (int)strlen(line) < size){
    fgets(line, size, file);
  }else if((int)strlen(line) == size){
    line = realloc(line, size*2);
    return 2;
  }else{
    free(line);
    return -1;
  }

  return 1;
}

int executeLine(int nOpz, char *cmd, char **opz){

  char *pathCmd = strcat("/bin/",cmd);
  pid_t pidSon = fork();
  opz = addArguments(nOpz, cmd, opz);

  switch(pidSon) {
    case -1:
      perror("fork() failed");
      free(opz);
      return 1;

    case 0:
      printf("Esecuzione di ls...\n");
      execl(pathCmd, cmd, opz, NULL);
      perror("exec failed");
      free(opz);
      return 1;

    default:
      wait(NULL);
      printf("%s completed\n",cmd);
  }
  free(opz);
  return 0;
}

char **addArguments(int cmdargc, char *line, char **cmdargv){
  char **newArgs = (char **)malloc((cmdargc   1) * sizeof(char *));

  for(int i = 0; i < cmdargc; i  ){
    if(strcmp(cmdargv[i], "@") == 0){
      newArgs[i] = line;
    }else{
      newArgs[i] = cmdargv[i];
    }
  }

  newArgs[cmdargc] = NULL;

  return newArgs;
}

file_exec.h

#ifndef FILEEX_H__
#define FILEEX_H__
#define BUZZ_SIZE 1024

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

int fileReadLine(char *, FILE *, int);
int executeLine(int, char *, char **);
char **addArguments(int, char *, char **);


#endif

esempio.txt

file1.txt
file2.txt
file3.txt

file1.txt

Hello

file2.txt

I'm

file3.txt

italian

Makefile

file_exec: file_exec.o main.o
    gcc -Wall -Werror -Wextra -std=c99 file_exec.o main.o -o file_exec
file_exec.o: file_exec.h file_exec.c
    gcc -Wall -Werror -Wextra -std=c99 -c file_exec.c
main.o: file_exec.h main.c
    gcc -Wall -Werror -Wextra -std=c99 -c main.c
clean:
    rm -f *.o main

(I'm on an Intel Mac)

The program should show this output from this command ./file_exec esempio.txt cat @:

Hello
I'm
italian

I've just tried to put some debugging printf after the first if statement on the main.c but it was ignored and not executed.

CodePudding user response:

You are using strcat the wrong way

char *pathCmd = strcat("/bin/",cmd);

is adding cmd to the end of string (constant array of char) "/bin". And then is returning this array. To be more accurate, what it does, is going at the address "/bin" - yes, that is an address - iterates 1 by 1 this address until it finds the terminal 0, that is the end of the string. And copy the content of address cmd there. And obviously, you can't do that. Not from a language point of view. Both "/bin" and cmd are pointers to char, as wanted. And the fact that "/bin" is a constant array of chars is not really a problem neither, since strcat doesn't change it: it only changes its content. But that string "/bin" is stored in an area of memory that your program is not supposed to try to write to. Which it does, when it tries to copy cmd after the second / of /bin/. Hence an execution error.

You need to allocate memory for pathCmd and concatenate there

char pathCmd[1024];
strcpy(pathCmd, "/bin/");
strcat(pathCmd, cmd);

Well, in reality, you should not do that neither. It is insecure (if cmd overflow the 1024 bytes I allocated here). So you should rather use strncat

char pathCmd[1024];
strcpy(pathCmd, "/bin/");
strncat(pathCmd, cmd, 1024-5); // 5 bytes already used by "/bin/"

Alternatively, you can use snprintf

char pathCmd[1024];
snprintf(pathCmd, 1024, "/bin/%s", cmd);

(it is 1 less line of code, and you don't need to compute the 1024-5 limit, since the limit is for the whole target)

Note that this has nothing to do with zsh. zsh is just the one complaining because you probably call this in a zsh script.

  • Related