CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/project/project-setup.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
This configures the project hub based on an environment variable or other data.
8
*/
9
10
import { existsSync } from "node:fs";
11
import { setPriority } from "node:os";
12
13
import { getLogger } from "@cocalc/project/logger";
14
const L = getLogger("project:project-setup");
15
16
// 19 is the minimum, we keep it 1 above that.
17
export const DEFAULT_FREE_PROCS_NICENESS = 18;
18
19
// this only lists some of the fields in use, there might be more
20
interface ProjectConfig {
21
quota?: {
22
member_host?: boolean;
23
dedicated_disks?: { name: string }[];
24
};
25
}
26
27
export function getProjectConfig(): ProjectConfig | null {
28
const conf_enc = process.env.COCALC_PROJECT_CONFIG;
29
if (conf_enc == null) {
30
return null;
31
}
32
try {
33
L.debug(`configure(${conf_enc.slice(0, 30)}...)`);
34
const conf_raw = Buffer.from(conf_enc, "base64").toString("utf8");
35
return JSON.parse(conf_raw);
36
} catch (err) {
37
// we report and ignore errors
38
L.debug(`ERROR parsing COCALC_PROJECT_CONFIG -- '${conf_enc}' -- ${err}`);
39
return null;
40
}
41
}
42
43
// this is for kucalc projects only
44
export function is_free_project(): boolean {
45
const conf = getProjectConfig();
46
const ifp = conf?.quota?.member_host === false;
47
L.debug(`is_free_project: ${ifp}`);
48
return ifp;
49
}
50
51
export function configure() {
52
if (is_free_project()) {
53
L.debug(`member_host is false -- renicing everything`);
54
setPriority(process.pid, DEFAULT_FREE_PROCS_NICENESS);
55
}
56
}
57
58
/**
59
* Set the given key/value pair in the environment.
60
* However, for $PATH we avoid breaking the project by prepending the new value to $PATH if there is no "$PATH" in the value,
61
* or we insert the existing value of $PATH where the string "$PATH" is found in the value as a placeholder.
62
*
63
* Ref: https://github.com/sagemathinc/cocalc/issues/7404
64
*/
65
function set_sanitized_envvar(key: string, value: string): string {
66
if (key === "PATH") {
67
if (value.indexOf("$PATH") !== -1) {
68
value = value.replace(/\$PATH/g, process.env.PATH || "");
69
} else {
70
value = `${value}:${process.env.PATH}`;
71
}
72
}
73
process.env[key] = value;
74
return value;
75
}
76
77
// Contains additional environment variables. Base 64 encoded JSON of {[key:string]:string}.
78
export function set_extra_env(): { [key: string]: string } | undefined {
79
sage_aarch64_hack();
80
81
if (!process.env.COCALC_EXTRA_ENV) {
82
L.debug("set_extra_env: nothing provided");
83
return;
84
}
85
86
const ret: { [key: string]: string } = {};
87
try {
88
const env64 = process.env.COCALC_EXTRA_ENV;
89
const raw = Buffer.from(env64, "base64").toString("utf8");
90
L.debug(`set_extra_env: ${raw}`);
91
const data = JSON.parse(raw);
92
if (typeof data === "object") {
93
for (let k in data) {
94
const v = data[k];
95
if (typeof v !== "string" || v.length === 0) {
96
L.debug(
97
`set_extra_env: ignoring key ${k}, value is not a string or has length 0`,
98
);
99
continue;
100
}
101
// this is the meat of all this – this should happen after cleanup()!
102
ret[k] = set_sanitized_envvar(k, v);
103
}
104
}
105
} catch (err) {
106
// we report and ignore errors
107
L.debug(
108
`ERROR set_extra_env -- cannot process '${process.env.COCALC_EXTRA_ENV}' -- ${err}`,
109
);
110
}
111
return ret;
112
}
113
114
// this should happen before set_extra_env
115
export function cleanup(): void {
116
// clean/sanitize environment to get rid of nvm and other variables
117
if (process.env.PATH == null) return;
118
process.env.PATH = process.env.PATH.split(":")
119
.filter((x) => !x.startsWith("/cocalc/nvm"))
120
.join(":");
121
// don't delete NODE_ENV below, since it's potentially confusing to have the value of NODE_ENV change
122
// during a running program.
123
// Also, don't delete DEBUG, since doing that in some cases breaks the debug library actually working,
124
// not surprisingly. Some additional cleanup is done wherever we spawn subprocesses,
125
// and then NODE_ENV and DEBUG are added back to being removed. See envForSpawn in
126
// @cocalc/backend/misc.
127
const envrm = [
128
"DATA",
129
"BASE_PATH",
130
"NODE_PATH",
131
"NODE_VERSION",
132
"NVM_CD_FLAGS",
133
"NVM_DIR",
134
"NVM_BIN",
135
"PATH_COCALC",
136
"COCALC_ROOT",
137
"DEBUG_CONSOLE",
138
];
139
envrm.forEach((name) => delete process.env[name]);
140
141
// Also get rid of any npm_ vars that get set due to how the project server
142
// is started. This is mainly an issue with cocalc-docker.
143
for (const key in process.env) {
144
if (key.startsWith("npm_")) delete process.env[key];
145
}
146
}
147
148
// See https://github.com/opencv/opencv/issues/14884
149
// Importing Sage in various situations, e.g., as is done for sage server,
150
// is fundamentally broken on aarch64 linux due to this issue. Yes, I explained
151
// this on sage-devel, but nobody understood.
152
// It's also important to NOT do this hack if you're not on aarch64!
153
function sage_aarch64_hack(): void {
154
const LD_PRELOAD = "/usr/lib/aarch64-linux-gnu/libgomp.so.1";
155
if (process.arch == "arm64" && existsSync(LD_PRELOAD)) {
156
process.env.LD_PRELOAD = LD_PRELOAD;
157
}
158
}
159
160