Skip to content

Instantly share code, notes, and snippets.

@jpdias
Last active May 18, 2021 12:09
Show Gist options
  • Save jpdias/c9d7f4b47d6611b45a621e847d6deb03 to your computer and use it in GitHub Desktop.
Save jpdias/c9d7f4b47d6611b45a621e847d6deb03 to your computer and use it in GitHub Desktop.
SOPE Exam 2020 Normal + Retake

O programa removebig remove de um diretório todos os ficheiros regulares cujo tamanho seja superior a um dado valor. Os argumentos do programa são o nome do diretório e o referido tamanho (exemplo de invocação para remover os ficheiros do diretório dir1 cujo tamanho seja superior a 1000000: removebig dir1 1000000). Nota: os subdiretórios devem ser ignorados.

a) Escreva a parte do código de removebig que percorre o referido diretório e, por cada ficheiro encontado cujo tamanho seja superior ao indicado, escreve o nome do ficheiro na saída padrão, incrementa um contador do número de ficheiros a remover (variável count) e invoca a função void remove(char *filename), cujo parâmetro é o nome do ficheiro a remover. Não implemente por enquanto esta função.

b) Escreva o código da função remove() que remove o ficheiro cujo nome recebe como parâmetro, recorrendo para isso a um subprocesso (processo filho) que executa o utilitário rm. Nota: não use chamadas system() .

c) Escreva a parte do código de removebig que espera que todos os processos filhos terminem e escreve na saída padrão o número total de ficheiros removidos.

Solution

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

int rmcount = 0;

// Pergunta 3.b.
int removefile(char* filename){
	
     char *const args[] = {"rm", filename, (char *)0};
     execv("/bin/rm", args);
}

int main(int argc, char *argv[])
{

    DIR *dir;
    struct dirent *entry;
    struct stat statbuf;

    if (!(dir = opendir(argv[1])))
        return 1;
   
    // Pergunta 3.a.
    while ((entry = readdir(dir)) != NULL)
    {
        
        stat(entry->d_name, &statbuf);
        //printf("%ld\n", statbuf.st_size);
        if (S_ISREG(statbuf.st_mode) && statbuf.st_size > atoi(argv[2]))
        {
            printf("%s\n", entry->d_name);
            rmcount++;
             
            if (fork() == 0)
            {
                //Visto que não eram dadas instruções especificas de
                //onde seria executado o programa, tanto podia ser passado
                //apenas o entry-d_name, como o path construido.
	            //
		        //  char *const args[] = {"rm", entry->d_name, (char *)0};
                //  execv("/bin/rm", args);
                //ou
                char *fullpath = malloc(strlen(argv[1])+strlen(entry->d_name)+1);
		        sprintf(fullpath, "%s/%s", argv[1], entry->d_name);
                removefile(fullpath);
            } 
        }
    }
    
    // Pergunta 3.c.
    pid_t kidpid;
    int status;

    while ((kidpid = wait(&status)) > 0){}

    printf("%d", rmcount);
}

Alternative Solution

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

int rmcount = 0;

// Pergunta 3.b.
int removefile(char* filename){
	if (fork() == 0){
        char *const args[] = {"rm", filename, (char *)0};
        execv("/bin/rm", args);
	}
}

int main(int argc, char *argv[])
{

    DIR *dir;
    struct dirent *entry;
    struct stat statbuf;

    if (!(dir = opendir(argv[1])))
        return 1;
   
    // Pergunta 3.a.
    while ((entry = readdir(dir)) != NULL)
    {
        
        stat(entry->d_name, &statbuf);
        //printf("%ld\n", statbuf.st_size);
        if (S_ISREG(statbuf.st_mode) && statbuf.st_size > atoi(argv[2]))
        {
            printf("%s\n", entry->d_name);
            rmcount++;
            
			//Visto que não eram dadas instruções especificas de
			//onde seria executado o programa, tanto podia ser passado
			//apenas o entry-d_name, como o path construido.
			//ou
			char *fullpath = malloc(strlen(argv[1])+strlen(entry->d_name)+1);
			sprintf(fullpath, "%s/%s", argv[1], entry->d_name);
			removefile(fullpath);
        }
    }
    
    // Pergunta 3.c.
    pid_t kidpid;
    int status;

    while ((kidpid = wait(&status)) > 0){}

    printf("%d", rmcount);
}

O utilitário stat dá informação sobre o ficheiro cujo nome lhe é passado como argumento. Por exemplo:

$ stat mykey.pub 
 File: mykey.pub 
 Size: 800       Blocks: 8      IO Block: 4096  regular file 
 Device: 804h/2052d    Inode: 10487631  Links: 1 
 Access: (0644/-rw-r--r--)  Uid: ( 1000/  jmcruz)  Gid: ( 1000/  jmcruz) 
 Access: 2020-01-22 10:51:14.245439691 +0000 
 Modify: 2017-06-22 13:27:41.000000000 +0100 
 Change: 2019-07-24 10:53:02.048964767 +0100 
 Birth:

a) Escreva a função main() de um programa que recebe como argumento da linha de comando um nome de um ficheiro, cria um processo filho o qual deve executar o utilitário stat, passando o referido ficheiro como argumento de execução. Notas: 1) o programa deve terminar com uma mensagem de erro se não for indicado o argumento; 2) o prompt da shell só deve ser mostrado após ser apresentada a saída de stat no ecrã.

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

int main(int argc, char *argv[])
{

    if(argc != 2){
        printf("Missing parameters");
        return 1;
    }

    pid_t pid;
    if ((pid = fork()) == 0)
    {
        char *const args[] = {"stat", argv[1], (char *)0};
        execv("/usr/bin/stat", args);
    }
    else
    {
        //wait for children (ensures that info appers in stdout)
        wait(NULL);
        return 0;
    }
}

b) Indique as alterações a introduzir na função main() (apenas as alterações, assinalando de forma clara a sua posição) de modo a filtrar a saída de stat, mostrando apenas as 2 primeiras linhas, isto é, as que começam por File: e por Size: (ver exemplo acima). Sugestão: execute o utilitário stat num coprocesso, conectado ao processo inicial através de um pipe "sem nome", recolha a sua saída e mostre apenas as 2 linhas referidas. Considere que lhe é fornecida uma função cujo protótipo é int readline(int fd, char *buffer) que lê uma linha de texto de uma stream fd, colocando o resultado no endereço especificado em buffer.

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

#define READ 0
#define WRITE 1

int readline(int fd, char *buffer)
{
    char *buf;
    char ch;
    buf = buffer;
    int count = 0;
    while (1)
    {
        int nread = read(fd, &ch, 1);
        count += nread;
        if (ch == '\n')
        {
            *buf++ = '\0';
            break;
        }
        *buf++ = ch;
    }
    return count;
}

int main(int argc, char *argv[])
{

    if (argc != 2)
    {
        printf("Missing parameters");
        return 1;
    }

    int fd[2];
    if (pipe(fd) == -1)
    {
        perror("Pipe failed");
        exit(1);
    }

    pid_t pid;
    if ((pid = fork()) == 0)
    {
        close(fd[READ]);                //closing pipe read
        dup2(fd[WRITE], STDOUT_FILENO); //replacing stdout with pipe write

        char *const args[] = {"stat", argv[1], (char *)0};
        execv("/usr/bin/stat", args);
        perror("exec failed");
        exit(1);
    }
    else
    {
        close(fd[WRITE]); //closing pipe write
        wait(NULL);
        for (int i = 0; i < 2; i++)
        {
            char *buffer;
            readline(fd[READ], buffer);
            printf("%s\n", buffer);
        }

        return 0;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment