ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
http_renderer_process.c File Reference

Go to the source code of this file.

Data Structures

struct  render_worker_t

Macros

#define MCP_RENDER_ENV_CAPACITY   16

Functions

void http_renderer_process (http_renderer_process_request_t *r, atomic_t *cancelled, http_renderer_process_result_t *result)
void http_renderer_process_result_destroy (http_renderer_process_result_t *result)

Macro Definition Documentation

◆ MCP_RENDER_ENV_CAPACITY

#define MCP_RENDER_ENV_CAPACITY   16

Definition at line 3 of file http_renderer_process.c.

Referenced by http_renderer_process().

Function Documentation

◆ http_renderer_process()

void http_renderer_process ( http_renderer_process_request_t * r,
atomic_t * cancelled,
http_renderer_process_result_t * result )

Definition at line 112 of file http_renderer_process.c.

113 {
114 memset(result, 0, sizeof(*result));
116 SAFE_STRNCPY(result->error, "Renderer output must select text or files", sizeof(result->error));
117 return;
118 }
119 render_worker_t w = {.cancelled = cancelled, .result = result, .started = time_get_ns(), .timeout = r->timeout_ms};
120 result->text = SAFE_CALLOC(HTTP_RENDERER_MAX_OUTPUT + 1, 1, char *);
121 if (uv_loop_init(&w.loop)) {
122 SAFE_STRNCPY(result->error, "Cannot initialize renderer event loop", sizeof(result->error));
123 return;
124 }
125 uv_pipe_init(&w.loop, &w.output, 0);
126 uv_pipe_init(&w.loop, &w.errors, 0);
127 uv_timer_init(&w.loop, &w.timer);
128 w.output.data = w.errors.data = w.timer.data = w.process.data = &w;
129 uv_stdio_container_t streams[3] = {0};
130 streams[0].flags = UV_IGNORE;
131 streams[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
132 streams[1].data.stream = (uv_stream_t *)&w.output;
133 streams[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
134 streams[2].data.stream = (uv_stream_t *)&w.errors;
135 // Only runtime essentials are inherited; user config, ASCII_CHAT_* and credentials are excluded.
136 const char *keys[] = {
137 "PATH", "SystemRoot", "WINDIR", "TEMP", "TMP", "TMPDIR", "LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", "TESTING", NULL};
138 char *env[MCP_RENDER_ENV_CAPACITY] = {0};
139 size_t ne = 0;
140 for (size_t i = 0; keys[i]; ++i) {
141 const char *v = platform_getenv(keys[i]);
142 if (v) {
143 size_t n = strlen(keys[i]) + strlen(v) + 2;
144 env[ne] = SAFE_MALLOC(n, char *);
145 SAFE_SNPRINTF(env[ne], n, "%s=%s", keys[i], v);
146 ++ne;
147 }
148 }
149 // The private staging directory provides an isolated home for media libraries.
150 if (r->stage_dir[0]) {
151 size_t n = strlen(r->stage_dir) + sizeof("HOME=");
152 env[ne] = SAFE_MALLOC(n, char *);
153 SAFE_SNPRINTF(env[ne], n, "HOME=%s", r->stage_dir);
154 ++ne;
155 }
156 env[ne++] = platform_strdup("ASCII_CHAT_MCP_WORKER=1");
157 env[ne++] = platform_strdup("ASCII_CHAT_OTEL_ENABLED_TELEMETRY=none");
158 if (r->capture_webcam)
159 env[ne++] = platform_strdup("ASCII_CHAT_MCP_WEBCAM=1");
160 uv_process_options_t opts = {
161 .exit_cb = exited, .file = r->argv[0], .args = r->argv, .env = env, .stdio_count = 3, .stdio = streams};
162 int rc = uv_spawn(&w.loop, &w.process, &opts);
163 for (size_t i = 0; i < ne; ++i)
164 SAFE_FREE(env[i]);
165 if (rc) {
166 SAFE_SNPRINTF(result->error, sizeof(result->error), "Cannot start renderer: %s", uv_strerror(rc));
167 uv_close((uv_handle_t *)&w.output, NULL);
168 uv_close((uv_handle_t *)&w.errors, NULL);
169 uv_close((uv_handle_t *)&w.timer, NULL);
170 } else {
171 uv_read_start((uv_stream_t *)&w.output, allocate, read_output);
172 uv_read_start((uv_stream_t *)&w.errors, allocate, read_output);
173 uv_timer_start(&w.timer, tick, 20, 20);
174 }
175 uv_run(&w.loop, UV_RUN_DEFAULT);
176 uv_loop_close(&w.loop);
177 result->elapsed_ms = (time_get_ns() - w.started) / 1000000;
178 if (rc || w.failed)
179 return;
180 result->error[0] = '\0';
181 if (!result->length && !r->stage_file[0]) {
182 SAFE_STRNCPY(result->error, "Renderer returned no ASCII art", sizeof(result->error));
183 return;
184 }
185 if (r->stage_file[0]) {
186 uv_fs_t fs;
187 int e = uv_fs_stat(NULL, &fs, r->stage_file, NULL);
188 bool valid = e == 0 && fs.statbuf.st_size > 0;
189 uv_fs_req_cleanup(&fs);
190 if (!valid) {
191 SAFE_STRNCPY(result->error, "Renderer did not create an output file", sizeof(result->error));
192 return;
193 }
194 // Hard-link publication is atomic and fails if any file/symlink already owns the destination.
195 // Staging beside the destination keeps this on the same filesystem.
196 e = uv_fs_link(NULL, &fs, r->stage_file, r->output_path, NULL);
197 uv_fs_req_cleanup(&fs);
198 if (e) {
199 SAFE_SNPRINTF(result->error, sizeof(result->error), "Cannot save without replacing an existing file: %s",
200 uv_strerror(e));
201 return;
202 }
203 SAFE_STRNCPY(result->output_path, r->output_path, sizeof(result->output_path));
204 }
205 http_render_result_t frames = {0};
206 if (result->text && result->length)
207 (void)http_render_result_add_text_frame(&frames, result->text, result->length, 0);
208 if (result->output_path[0])
209 (void)http_render_result_add_file_frame(&frames, result->output_path, 0);
210 result->frames = frames.frames;
211 result->frame_count = frames.frame_count;
212}
bool valid
Definition fft.c:27
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:507
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
const char * platform_getenv(const char *name)
Get an environment variable value.
asciichat_error_t http_render_result_add_file_frame(http_render_result_t *result, const char *path, uint64_t timestamp_ms)
asciichat_error_t http_render_result_add_text_frame(http_render_result_t *result, const char *text, size_t length, uint64_t timestamp_ms)
#define HTTP_RENDERER_MAX_OUTPUT
@ HTTP_RENDER_OUTPUT_FILE
@ HTTP_RENDER_OUTPUT_TEXT
#define MCP_RENDER_ENV_CAPACITY
http_render_frame_t * frames
http_render_output_type_t output_type
char stage_file[PLATFORM_MAX_PATH_LENGTH]
char stage_dir[PLATFORM_MAX_PATH_LENGTH]
char output_path[PLATFORM_MAX_PATH_LENGTH]
char output_path[PLATFORM_MAX_PATH_LENGTH]
http_render_frame_t * frames

References http_renderer_process_request_t::argv, http_renderer_process_request_t::capture_webcam, http_renderer_process_result_t::elapsed_ms, http_renderer_process_result_t::error, render_worker_t::errors, render_worker_t::failed, http_render_result_t::frame_count, http_renderer_process_result_t::frame_count, http_render_result_t::frames, http_renderer_process_result_t::frames, HTTP_RENDER_OUTPUT_FILE, HTTP_RENDER_OUTPUT_TEXT, http_render_result_add_file_frame(), http_render_result_add_text_frame(), HTTP_RENDERER_MAX_OUTPUT, http_renderer_process_result_t::length, render_worker_t::loop, MCP_RENDER_ENV_CAPACITY, render_worker_t::output, http_renderer_process_request_t::output_path, http_renderer_process_result_t::output_path, http_renderer_process_request_t::output_type, platform_getenv(), render_worker_t::process, SAFE_CALLOC, SAFE_FREE, SAFE_MALLOC, SAFE_SNPRINTF, SAFE_STRNCPY, http_renderer_process_request_t::stage_dir, http_renderer_process_request_t::stage_file, render_worker_t::started, http_renderer_process_result_t::text, http_renderer_process_request_t::timeout_ms, render_worker_t::timer, and valid.

◆ http_renderer_process_result_destroy()

void http_renderer_process_result_destroy ( http_renderer_process_result_t * result)

Definition at line 214 of file http_renderer_process.c.

214 {
215 if (!result)
216 return;
217 for (size_t i = 0; i < result->frame_count; ++i)
218 SAFE_FREE(result->frames[i].text);
219 SAFE_FREE(result->frames);
220 SAFE_FREE(result->text);
221 memset(result, 0, sizeof(*result));
222}

References http_renderer_process_result_t::frame_count, http_renderer_process_result_t::frames, SAFE_FREE, http_render_frame_t::text, and http_renderer_process_result_t::text.