/*
    zserial_proc_pty - portable serial port API
	Module for PTY process
    Copyright (C) 2012-2023 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include <zserial.h>

#ifdef Z_HAVE_PTY_H

#include <zdebug.h>
#include <zerror.h>
#include <ztime.h>

#include <errno.h>
#include <pty.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef Z_HAVE_UNISTD_H
#include <unistd.h>
#endif


static int zserial_proc_pty_open(struct zserial *zser){
    int err;
    char *c;

    // dbg("zserial_open pid=%d master=%d\n", zser->pid, zser->master);
    if (zser->pid > 0) return 0; 
    zser->pid = forkpty(&zser->master, NULL, NULL, NULL); //FIXME &sw->ws);
    if (zser->pid < 0) return -1;
    if (zser->pid == 0){ /* child */
        int i;

        setenv("TERM", "dumb", 1);
        for (i = 3; i < FD_SETSIZE; i++) close(i);
        /*execlp("/bin/sh", "/bin/sh", "-c", cmd, NULL);*/
        execlp(zser->cmd, zser->cmd, zser->arg, NULL);
        err = errno;
        c = z_strdup_strerror(err);
        fprintf(stderr, "*** failed exec '%s' errno=%d %s\n", zser->cmd, errno, c);
        g_free(c);
        exit(-1);
    }
    zser->opened = 1;

    //dbg("zserial_open: forkpty()=%d master=%d\n", zser->pid, zser->master);
    return 0;
}

static int zserial_proc_pty_read(struct zserial *zser, void *data, int len, int timeout_ms){
    int ret;

    ret = read(zser->master, data, len);
    if (ret < 0){
        g_string_printf(zser->errorstr, "Can't read from %s (PTY fd %d): ", zser->id, zser->master);
        z_strerror(zser->errorstr, errno);
        zserial_close(zser);
    }
    return ret;
}

static int zserial_proc_pty_write(struct zserial *zser, void *data, int len){
    int ret;
    
    ret = write(zser->master, data, len);
    dbg("zserial_write PTY(%d) = %d\n", zser->master, len);
    if (ret < 0){
        g_string_printf(zser->errorstr, "Can't write to %s: ", zser->id);
        z_strerror(zser->errorstr, errno);
        zserial_close(zser);
    }
    return ret;
}

static int zserial_proc_pty_close(struct zserial *zser){
    int ret = 0;
    dbg("zserial_close() PTY pid=%d\n", zser->pid);
    if (zser->pid > 0){
        //dbg("kill(%d, %d)\n", zser->pid, SIGTERM);
        kill(zser->pid, SIGTERM);
        zser->pid = 0;
    }
//    dbg("pid=%d\n", zser->pid);
    if (zser->master >= 0){
        ret = close(zser->master);
    }
    return ret;
}
#endif

struct zserial *zserial_init_proc_pty(const char *cmd, const char *arg){
#ifdef Z_HAVE_PTY_H
	char *c;
	struct zserial *zser = zserial_init();
	zser->type = ZSERTYPE_PROC_PTY;
	zser->id = g_strdup(cmd);
	c = strchr(zser->id, ' ');
	if (c != NULL) *c = '\0';
	zser->cmd = g_strdup(cmd);
    zser->arg = g_strdup(arg);
    dbg("zserial_init_process_pty ('%s','%s')\n", cmd, arg);

    zser->zs_open = zserial_proc_pty_open;
    zser->zs_read = zserial_proc_pty_read;
    zser->zs_write = zserial_proc_pty_write;
    zser->zs_close = zserial_proc_pty_close;
    return zser;
#else
	return NULL;
#endif
}

