1/*
2 * Copyright (c) 2013, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
8 * Attn: Systems Group.
9 */
10
11#include <angler/angler.h>
12#include <barrelfish/barrelfish.h>
13#include <barrelfish/debug.h>
14#include <barrelfish/spawn_client.h>
15#include <posixcompat.h>
16#include <vfs/vfs.h>
17
18#include <assert.h>
19#include <stdio.h>
20#include <unistd.h>
21
22#define SHELL "fish"
23#define BUFFER_SIZE 128
24
25static int openpty(int *amaster, int *aslave)
26{
27    int ptm;
28    char *pts;
29
30    /* open master side */
31    ptm = posix_openpt(O_RDWR | O_NOCTTY);
32    if (ptm < 0) {
33        return -1;
34    }
35    if (grantpt(ptm) < 0) {
36        return -1;
37    }
38    if (unlockpt(ptm) < 0) {
39        return -1;
40    }
41    *amaster = ptm;
42
43    pts = ptsname(ptm);
44    if (pts == NULL) {
45        return -1;
46    }
47
48    /* open slave side */
49    *aslave = open(pts, O_RDWR | O_NOCTTY);
50    if (*aslave < 0) {
51        close(*amaster);
52        return -1;
53    }
54
55    return 0;
56}
57
58static void spawn_shell(int ttyfd)
59{
60    struct capref session_id;
61    errval_t err;
62    coreid_t my_core_id = disp_get_core_id();
63    char *shell = SHELL;
64
65    /* establish a new session */
66    iref_t iref = posixcompat_pts_get_iref(ttyfd);
67    err = angler_new_session_with_iref(iref, &session_id);
68    if (err_is_fail(err)) {
69        USER_PANIC_ERR(err, "Error starting session.");
70    }
71    debug_printf("Created a new session.\n");
72
73    /* setup argv of the shell */
74    char *shell_argv[2];
75    shell_argv[0] = SHELL;
76    shell_argv[1] = NULL;
77
78    /* inherit the session capability */
79    struct capref inheritcn_cap;
80    err = alloc_inheritcn_with_caps(&inheritcn_cap, NULL_CAP, session_id, NULL_CAP);
81    if (err_is_fail(err)) {
82        USER_PANIC_ERR(err, "Error allocating inherit CNode with session cap.");
83    }
84
85    /* spawn shell on the same core */
86    debug_printf("About to spawn fish.\n");
87    err = spawn_program_with_caps(my_core_id, shell, shell_argv, NULL,
88                                  inheritcn_cap, NULL_CAP, SPAWN_FLAGS_NEW_DOMAIN,
89                                  NULL);
90    if (err_is_fail(err)) {
91        USER_PANIC_ERR(err, "Error spawning shell.");
92    }
93}
94
95int main(int argc, char *argv[])
96{
97    int ret = 0;
98    int ptm = 0;
99    int pts = 0;
100
101    vfs_init();
102
103    // allocate new pseudo-terminal
104    ret = openpty(&ptm, &pts);
105    debug_printf("Allocated pseudo-terminal with fds %d and %d.\n", ptm, pts);
106    assert(ret == 0);
107
108    // spawn shell
109    spawn_shell(pts);
110
111    // read from master side
112    char buffer[BUFFER_SIZE + 1];
113    size_t r = 0;
114    while (true) {
115        debug_printf("Waiting for characters at pseudo-terminal master.\n");
116        r = read(ptm, buffer, BUFFER_SIZE);
117        if (r > 0) {
118            buffer[r] = '\0';
119            printf("read %zd characters: %s\n.", r, buffer);
120        } else {
121            printf("error: read returned %zd\n", r);
122        }
123    }
124
125    return 0;
126}
127