/* EXERCISE 08 — BROKEN ON PURPOSE. Do not copy this shape into real code.
 *
 * An export feature streams a document to a helper process over a pipe. The
 * helper does the conversion and is expected to outlive the transfer.
 *
 * Symptom as reported by the field: "if the helper crashes mid-export, our app
 * vanishes too. No alert, no crash report we can read, nothing in our logs —
 * the process is simply gone. It only happens when the helper dies first."
 *
 * Build and run:
 *   clang -O2 -g -Wall -Wextra helperlink.c -o /tmp/helperlink_broken
 *   /tmp/helperlink_broken ; echo "exit status: $?"
 *
 * EXPECTED: this program is KILLED BY A SIGNAL. From a shell that is exit
 * status 141, which is 128 + 13, and 13 is SIGPIPE. That is the whole bug:
 * the default disposition of SIGPIPE is to terminate the process, so a write
 * to a pipe with no reader is not an error your code gets to handle — it is
 * the end of your process.
 *
 * Every measurement is printed as key=value so a script can assert on it.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>

#define CHUNK        8192
#define CHUNKS       4000       /* ~32 MB of document                      */
#define HELPER_DIES_AFTER 40    /* chunks the helper consumes before dying */
#define WATCHDOG_S   60

static void *watchdog(void *unused) {
    (void)unused;
    sleep(WATCHDOG_S);
    const char m[] = "WATCHDOG: no progress within budget - forcing _exit(75).\n";
    ssize_t ignored = write(2, m, sizeof m - 1); (void)ignored;
    _exit(75);
}

int main(void) {
    pthread_t wd;
    pthread_create(&wd, NULL, watchdog, NULL);
    pthread_detach(wd);
    setvbuf(stdout, NULL, _IOLBF, 0);

    int p[2];
    if (pipe(p) != 0) { perror("pipe"); return 1; }

    pid_t helper = fork();
    if (helper == 0) {
        /* The helper: reads a few chunks, then dies the way a crashing
         * converter does — abruptly, without closing anything politely. */
        close(p[1]);
        char *b = malloc(CHUNK);
        for (int i = 0; i < HELPER_DIES_AFTER; i++) {
            ssize_t r = read(p[0], b, CHUNK);
            if (r <= 0) break;
        }
        _exit(9);               /* helper is gone; its pipe end is closed */
    }
    close(p[0]);

    char *doc = malloc(CHUNK);
    memset(doc, 'D', CHUNK);

    long written = 0;
    printf("phase=streaming\n");
    for (int i = 0; i < CHUNKS; i++) {
        /* No error from this call is ever observed when the reader is gone,
         * because the process does not survive long enough to read errno. */
        ssize_t w = write(p[1], doc, CHUNK);
        if (w < 0) {
            printf("phase=write-error errno=%d message=%s\n", errno, strerror(errno));
            break;
        }
        written += w;
    }

    printf("phase=finished bytesWritten=%ld\n", written);
    printf("helperLost=0\n");
    printf("reportedCleanly=1\n");
    close(p[1]);
    int st; waitpid(helper, &st, 0);
    free(doc);
    return 0;
}
