//有思工作室编程类,用于Windows下面的C语言。 //说明:nodejs很多命令默认是在linux下面写的,所以windows平台的信息有些不准确。 //不能调用spawn命令,spawn是最原始的,不能设置超时时间,但spawn可以用数组,这点方便。 //知识要点:CL为编译命令。[]数组中为参数选项 // /nolog为不输出微软的标志。 //哎,用GCC吧。等以后node稳定了再做windows版本。 var fs = require('fs'); var exec = require('child_process').exec, free; //var exec = require('child_process').exec, free = exec('CL', ['hello.c', '/nologo']); //var spawn = require('child_process').spawn, free = spawn('hello.exe', ['hello.c']); //var spawn = require('child_process').spawn, free = spawn('free', ['-m']); //------------------------------------------------执行PHP等解释型语言,解释执行的必须加chroot做沙箱------------------------------------------------------------ //PHP语言 exports.exephp = function (res, f,type) { //编译指令//生成gcc命令。 //再执行此执行文件,chroot是让命令执行在沙箱中。沙箱命令需要去除目录标识。 var cmd = "chroot ./output php ./userfiles/" + f + ".php"; console.log(cmd); //解释性语言异步执行编译生成的命令为空。 interpreLang(res, cmd , f, type); } //Python语言 exports.exepython = function (res, f,type) { var cmd = "chroot ./output python ./userfiles/" + f + ".py"; console.log(cmd); interpreLang(res, cmd, f, type); ; //解释性语言异步执行编译生成的命令为空。 } //Nodejs语言 exports.exenodejs = function (res, f,type) { var cmd = "chroot ./output node ./userfiles/" + f + ".js"; console.log(cmd); interpreLang(res, cmd, f, type); ; //解释性语言异步执行编译生成的命令为空。 } //Ruby语言 exports.exeruby = function (res, f,type) { var cmd = "chroot ./output ruby ./userfiles/" + f + ".rb"; console.log(cmd); interpreLang(res, cmd, f, type); ; //解释性语言异步执行编译生成的命令为空。 } //Perl语言 exports.exeperl = function (res, f,type) { var cmd = "chroot ./output perl ./userfiles/" + f + ".pl"; console.log(cmd); interpreLang(res, cmd, f, type); ; //解释性语言异步执行编译生成的命令为空。 } //Lisp语言 exports.exelisp = function (res, f,type) { var cmd = "chroot ./output clisp ./userfiles/" + f + ".lisp"; console.log(cmd); interpreLang(res, cmd, f, type); ; //解释性语言异步执行编译生成的命令为空。 } //解释性语言统一调用函数 interpreLang = function (res, cmd,f, type) { var output = ""; //最终返回的错误信息,警告信息,执行成功信息以及生成的exe的执行信息。但如何区分? //解释性语言异步执行编译生成的命令为空。 usexefile(res, cmd, "", f, output, type, interpreLangAsyn); } interpreLangAsyn = function (res, output,f, acmd, type) { output = "运行环境Linux+" + type + "语言:\n" + output; res.end(output); //php很简单,没C复杂 } //------------------------------------------------执行Gcc Net Mono等编译型----------------------------------------------------- //执行Net和Mono //mono还需要 /proc才能运行,需要把/proc mount到 chroot目录 //首先在choot目录下建立文件夹 /proc。 //mount proc ./proc -t proc exports.exenet = function (res, f,type) { //Mono编译太慢,需要调整时间为5秒 f = './output/userfiles/' + f; //给文件加上目录名 var cmd = 'mcs ' + f + '.cs'; //再执行此执行文件,chroot是让命令执行在沙箱中。沙箱命令需要去除目录标识。异步执行的命令 var acmd = "chroot ./output mono ./userfiles/" + f.slice(19) + ".exe"; execomplie(res, f, cmd, acmd,type); } //执行Gcc exports.exegcc = function (res, f,type) { //编译指令//生成gcc命令。 f = './output/userfiles/' + f; //给文件加上目录名 var cmd = 'gcc ' + f + '.c' + ' -o ' + f + '.exe'; //再执行此执行文件,chroot是让命令执行在沙箱中。沙箱命令需要去除目录标识。异步执行的命令 var acmd = "chroot ./output ./userfiles/" + f.slice(19) + ".exe"; execomplie(res, f, cmd, acmd,type); } //------------------------------------------------------编译型命令封装------------------------------------------- //res是传递的http参数,f是文件,c是需要执行的编译命令,as是编译成功后执行可执行的exe文件命令 execomplie = function (res, f,cmd,acmd,type) { //编译指令//生成gcc命令。 var output = ""; //最终返回的错误信息,警告信息,执行成功信息以及生成的exe的执行信息。但如何区分? usexefile(res, cmd, acmd, f, output,type, execcompileAsyn); } //执行Gcc命令回调函数。参数1为执行gcc的返回数据,参数f为文件名。acmd是执行编译成功后可执行文件的命令 execcompileAsyn = function (res, output, f,acmd,type) { //如何判断编译成功呢?暂时采用是否生成了可执行文件的方式。如果采用这种方式,好像windows平台也能做,但有问题,生成成功后再次生成即使错误也会判断成功。 //对了,同时判断是否有错误输出就OK了。前台判断好还是后台判断好? //加Sync表示同步,此处异步好还是同步好呢? if (builderr(output)) res.end(output); else { if (fs.existsSync(f + '.exe')) { output += "运行环境Linux+" + type + "语言:\n"; //再执行此执行文件,把异步命令作为执行命令,原来的异步命令为空。 usexefile(res, acmd,"", f, output,type, execexeAsyn); //异步执行生成的exe文件。 } } } //执行C语言可执行文件回调函数 execexeAsyn = function (res, output, f) { //删除执行文件,准备下次运行。此处不好整理逻辑。 res.end(output); } //判断编译是否发生错误,如果发送错误,则输出true,否则输出false builderr = function (output) { var z = output.replace(/\r\n/g, '\n').split('\n'), i; for (i = 0; i < z.length; i++) { if (z[i].indexOf('标准错误输出') > -1) return true; } return false; } //--------------------------------------执行控制台命名封装--------------------------------------- //判断用户是否输出了加大的文字量,如果用户恶意输入,给他提示,结束程序。 largeoutput = function (res,output) { if (output.length > 10000) res.end("哥们,别开玩笑!\n"+output); } //雨云科技执行文件函数,cmd是nycodex.com命令,callback是执行完毕后的回调函数 usexefile = function (res, cmd, acmd, f, output,type, callback) { //var output = ""; //输出,因为stderr或者stdout会触发多次,用一个变量循环,为啥要触发多次,以后研究。 //-Wall 把所有编译警告全部显示。-o指定输出目录 //free = exec('gcc -Wall ./output/hello.c -o ./output/h.exe', free = exec(cmd, { encoding: 'utf8', //编码设置 //encoding: null, //编码设置 timeout: 8000, //超时时间设置。 maxBuffer: 200 * 1024, killSignal: 'SIGTERM', cwd: null, env: process.env //windows环境设置或者linux环境设置,好像不成功 }); // 捕获标准输出并将其打印到控制台 free.stdout.on('data', function (data) { //console.log('标准输出:\n' + data);//data != "\n" ? output += "标准输出: " + data : output += data; //给输出前面加上中文标示,便于学生理解 output += data; //res.write(data); //if(output.length>1) res.end(output); //largeoutput(res, output); //res.end("AAAAAA"); }); // 捕获标准错误输出并将其打印到控制台 //console.log('标准错误输出:\n' + data); free.stderr.on('data', function (data) { (data != "\n"&&data != "\r") ? output += ("标准错误输出:" + data) : output += data; //给输出前面加上中文标示,便于学生理解。 }); // 注册子进程关闭事件 //console.log('子进程已退出,代码:' + code); console.log(output); free.on('exit', function (code, signal) { largeoutput(res, output); if (callback) callback(res, output, f,acmd,type); //如果有定义回调函数,则执行回调函数。 }); //free.stdin.write("4\n"); //C语言处理标准输入,非常麻烦,暂时留在这里。这应该封装为一个函数。首先用resume恢复以前的进程,不过首先需要找到该进程。 //free.stdin.resume(); //这句话是为了不让控制台推出 //free.stdin.on('data', function (data) { //console.log('标准输出:\n' + data);//data != "\n" ? output += "标准输出: " + data : output += data; //给输出前面加上中文标示,便于学生理解 //free.stdout.write('标准输入:' + data); //free.stdin.emit('end'); //output += "本系统暂时不支持scanf输入语句,有高手能实现吗?请在意见反馈中联系我们,谢谢"; //res.end(output); //free.stdout.write('data:'+data); //}); //free.stdin.on('end', function () { //if(output.length>1) res.end(output); //output+="input end"; //free.stdout.write('end'); //output += "本系统暂时不支持scanf输入语句,有高手能实现吗?请在意见反馈中联系我们,谢谢"; //}); }