diff --git a/api/src/controllers/internal/Session.ts b/api/src/controllers/internal/Session.ts index 032363b..75e258c 100644 --- a/api/src/controllers/internal/Session.ts +++ b/api/src/controllers/internal/Session.ts @@ -206,12 +206,15 @@ ${autoExecContent}` export const getSessionController = ( runTime: RunTimeType ): SessionController => { - if (process.sessionController) return process.sessionController + if (runTime === RunTimeType.SAS) { + process.sasSessionController = + process.sasSessionController || new SASSessionController() + + return process.sasSessionController + } process.sessionController = - runTime === RunTimeType.SAS - ? new SASSessionController() - : new SessionController() + process.sessionController || new SessionController() return process.sessionController } diff --git a/api/src/controllers/internal/processProgram.ts b/api/src/controllers/internal/processProgram.ts index dc7ff45..206f429 100644 --- a/api/src/controllers/internal/processProgram.ts +++ b/api/src/controllers/internal/processProgram.ts @@ -1,6 +1,6 @@ import path from 'path' -import fs from 'fs' -import { execFileSync } from 'child_process' +import { WriteStream, createWriteStream } from 'fs' +import { execFile } from 'child_process' import { once } from 'stream' import { createFile, moveFile } from '@sasjs/utils' import { PreProgramVars, Session } from '../../types' @@ -105,26 +105,58 @@ export const processProgram = async ( throw new Error('Invalid runtime!') } - try { - await createFile(codePath, program) + await createFile(codePath, program) - // create a stream that will write to console outputs to log file - const writeStream = fs.createWriteStream(logPath) - // waiting for the open event so that we can have underlying file descriptor - await once(writeStream, 'open') - execFileSync(executablePath, [codePath], { - stdio: ['ignore', writeStream, writeStream] + // create a stream that will write to console outputs to log file + const writeStream = createWriteStream(logPath) + // waiting for the open event so that we can have underlying file descriptor + await once(writeStream, 'open') + + await execFilePromise(executablePath, [codePath], writeStream) + .then(() => { + session.completed = true + process.logger.info('session completed', session) }) - // copy the code file to log and end write stream - writeStream.end(program) - session.completed = true - process.logger.info('session completed', session) - } catch (err: any) { - session.completed = true - session.crashed = err.toString() - process.logger.error('session crashed', session.id, session.crashed) - } + .catch((err) => { + session.completed = true + session.crashed = err.toString() + process.logger.error('session crashed', session.id, session.crashed) + }) + + // copy the code file to log and end write stream + writeStream.end(program) } } +/** + * Promisified child_process.execFile + * + * @param file - The name or path of the executable file to run. + * @param args - List of string arguments. + * @param writeStream - Child process stdout and stderr will be piped to it. + * + * @returns {Promise<{ stdout: string, stderr: string }>} + */ +const execFilePromise = ( + file: string, + args: string[], + writeStream: WriteStream +): Promise<{ stdout: string; stderr: string }> => { + return new Promise((resolve, reject) => { + const child = execFile(file, args, (err, stdout, stderr) => { + if (err) reject(err) + + resolve({ stdout, stderr }) + }) + + child.stdout?.on('data', (data) => { + writeStream.write(data) + }) + + child.stderr?.on('data', (data) => { + writeStream.write(data) + }) + }) +} + const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) diff --git a/api/src/types/system/process.d.ts b/api/src/types/system/process.d.ts index be120a2..6eb65e8 100644 --- a/api/src/types/system/process.d.ts +++ b/api/src/types/system/process.d.ts @@ -9,6 +9,7 @@ declare namespace NodeJS { logsLoc: string logsUUID: string sessionController?: import('../../controllers/internal').SessionController + sasSessionController?: import('../../controllers/internal').SASSessionController appStreamConfig: import('../').AppStreamConfig logger: import('@sasjs/utils/logger').Logger runTimes: import('../../utils').RunTimeType[]