C - ненужные символы на выходе

Допустим, у меня есть две программы - input.c и output.c. Все, что я хочу сделать, это отправить некоторую полезную нагрузку/символы в формате "половина пирамиды" в другую с помощью функции execl().

input.c

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

#define SIZE 1024

int length;

int main(int argc, char *argv[])
{
    pid_t pid;
    char *target;
    //char payload[length+1];
    char payload[SIZE + 1];
    int status;
    int i = 0;

    if(argc < 2)
    {
        printf("Usage %s <length> <target>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    length = atoi(argv[1]);
    target = argv[2];

    while(i < length)
    {
        pid = fork();

        if(pid != 0)
        {
            waitpid(-1, &status, 0);
            //exit(0);
        }

        if(pid == 0)
        {
            payload[i] = 'A';
            payload[i + 1] = '\0';
            execl(target, target, payload, NULL);
            //printf("%s\n", payload);
        }
        ++i;
    }
    return 0;
}

Закомментированные отрывки предназначены только для целей отладки. Т.к. как видите (при попытке), когда хочешь просто распечатать, все работает исправно.

output.c (или, если хотите, 'target.c')

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char buffer[30];
    strncpy(buffer, argv[1], sizeof(buffer));
    printf("Output: %s\n", buffer);

    return 0;
}

Когда я компилирую input.c, например:

gcc input.c -o input

& output.c:

gcc output.c -o output

Ok. Теперь все подготовлено. Скажем, я хочу отправить полезную нагрузку - длина 6

./input 6 ./output

но все, что я получаю на выходе, это только это (или просто с другими символами джонки):

Output: A
Output: 0A
Output: 0,A
Output: 0,�A
Output: 0,�8A
Output: 0,�8�A

Я пробовал так много вещей, но все они потерпели неудачу, а результат был таким же, как вы можете видеть выше.

Я был бы очень признателен, если бы кто-нибудь мог мне помочь и, возможно, показать мне, где проблема. Может быть проблема в совместном использовании функций fork() и execl()?


person Yeez    schedule 16.04.2015    source источник


Ответы (2)


Понятно, вам не следует обновлять payload в коде дочернего блока...

Вот исправление (сейчас не могу проверить):

 while(i < length)
    {
        pid = fork();
        payload[i] = 'A';
        payload[i + 1] = '\0';

        if(pid != 0)
        {
            waitpid(-1, &status, 0);
            //exit(0);
        }

        if(pid == 0)
        {
            execl(target, target, payload, NULL);
            //printf("%s\n", payload);
        }
        ++i;
    }

[удалено несвязанное предложение]

РЕДАКТИРОВАТЬ (дополнительные пояснения): payload обновление должно быть в как родительском, так и дочернем коде. Если вы не понимаете, почему, я могу добавить больше пояснений.

EDIT2 (по запросу). Вы хотите обновить полезную нагрузку для следующего разветвленного дочернего процесса. В вашем коде весь дочерний код заменяется кодом execl() на код target. Таким образом, fork() выполняется только первым родительским процессом (корневым).

Вы должны обновить полезную нагрузку первым родителем и сделать ее доступной для всех дочерних элементов. Поместить его в этот блок тоже не получится:

// block only executed by the first parent.
if(pid != 0)
{
    waitpid(-1, &status, 0);
}

Следовательно, вы должны обновить его в месте, доступном как для родителя, так и для ребенка: после блока fork(), перед блоком if(pid == 0).

В вашем коде вы увеличиваете i в общем блоке, но родительский payload никогда не обновляется. Таким образом, в дочернем блоке, непосредственно перед execl(), вы добавляете 'A\0' в конец неинициализированной строки C.

person Amessihel    schedule 16.04.2015
comment
Ты мужчина! Да, это работает как шарм. Большое спасибо за твою помощь. И да, если это не проблема для вас, я был бы признателен за более подробное объяснение, потому что я не думаю, что понимаю это на 100%, и я не хочу совершать подобную ошибку в будущем. - person Yeez; 16.04.2015
comment
Хорошо, добавлю пояснений. Для меня это была хорошая освежающая тренировка. - person Amessihel; 16.04.2015
comment
Хе-хе, я рад, что это так. Так или иначе, сейчас я проверю это еще раз и у меня есть один дополнительный вопрос - если вы можете ответить. В случае, если я заменю strncpy на strcpy и сделаю ./input 80 ./output, почему я не вижу где-то ошибки сегментации вывода? Мне просто интересно. - person Yeez; 16.04.2015
comment
@Yeez Это более удобно? Поскольку argv[1] — это строка с правильным завершением, а размер ./output меньше 30. - person Amessihel; 16.04.2015
comment
Я понимаю, это. Но я думаю о том, когда я отправлю туда длину › 30, и я хотел бы увидеть, когда программа выйдет из строя. В этом случае просто выполняются все итерации, и неважно, 40 это или 80 - потому что я не вижу никакой ошибки. Вы понимаете, что я хочу сказать? - person Yeez; 16.04.2015
comment
@Yeez Вы не получите ошибок, пока будете писать в память, выделенную вашему процессу ... Это все, что я могу сказать: поведение непредсказуемо. Вы также можете заглянуть в эту тему SO. - person Amessihel; 16.04.2015
comment
Хорошо, я проверю. Это все с моей стороны. Еще раз спасибо за большую помощь. - person Yeez; 16.04.2015

Когда ваша программа разветвляется, она создает новый процесс. Этот новый процесс после if(pid == 0) изменяет payload и запускает exec (т. е. выполняет output, который затем умирает. То есть ваше изменение полезной нагрузки остается в дочернем процессе и не влияет на родительский процесс. Только ++i влияет, поэтому вы Вы видите унифицированные данные.

Переместите изменение полезной нагрузки перед разветвлением (или, по крайней мере, из дочернего блока), чтобы оно также было в родительском блоке.

person che    schedule 16.04.2015