From 6ef03b4c189ee96d3908fe7aa229b7ee23ba2baf Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 16 Nov 2023 10:48:24 +0100 Subject: [PATCH 1/2] introduce EMC2_TMP_DIR similar to other EMC2_*_DIR --- src/Makefile.inc.in | 1 + src/configure.ac | 9 +++++++++ src/emc/task/backtrace.cc | 3 ++- src/emc/task/signalhandler.cc | 2 +- src/emc/tooldata/tooldata_mmap.cc | 3 ++- src/hal/classicladder/files_project.c | 3 ++- tcl/linuxcnc.tcl.in | 1 + 7 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Makefile.inc.in b/src/Makefile.inc.in index 42a207c8dbc..41bc4167195 100644 --- a/src/Makefile.inc.in +++ b/src/Makefile.inc.in @@ -17,6 +17,7 @@ LIB_DIR=@EMC2_HOME@/lib #used for install stuff #but have them here as reference #build system only uses EMC2_RTLIB_DIR when creating rtapi.conf +EMC2_TMP_DIR=@EMC2_TMP_DIR@ EMC2_BIN_DIR=@EMC2_BIN_DIR@ EMC2_TCL_DIR=@EMC2_TCL_DIR@ EMC2_HELP_DIR=@EMC2_HELP_DIR@ diff --git a/src/configure.ac b/src/configure.ac index 05da4d05812..3a8ca0d28a6 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -748,6 +748,7 @@ AC_DEFINE_UNQUOTED([EMC2_PO_DIR], "$EMC2_PO_DIR", [Directory for po/mo translati AC_DEFINE_UNQUOTED([EMC2_NCFILES_DIR], "$EMC2_NCFILES_DIR", [Directory for nc files]) AC_DEFINE_UNQUOTED([EMC2_IMAGE_DIR], "$EMC2_IMAGE_DIR", [Directory for images]) +AC_SUBST([EMC2_TMP_DIR]) AC_SUBST([EMC2_BIN_DIR]) AC_SUBST([EMC2_TCL_DIR]) AC_SUBST([EMC2_TCL_LIB_DIR]) @@ -771,6 +772,14 @@ LINUXCNC_AUX_EXAMPLES=/usr/share/linuxcnc/aux_examples AC_SUBST([LINUXCNC_AUX_GLADEVCP]) AC_SUBST([LINUXCNC_AUX_EXAMPLES]) +# define temporary directory +AC_ARG_WITH(tmpdir, + [ --with-tmpdir=PATH Specify path for temporary files], + tmpdir="$withval", + tmpdir="/tmp") +AC_DEFINE_UNQUOTED(EMC2_TMP_DIR, "$tmpdir/linuxcnc", [Temporary files directory]) +AC_SUBST(EMC2_TMP_DIR, [$tmpdir]) + ############################################################################## # Subsection 3.5 - check for GTK # # FIXME: allow it to be enabled or disabled command line # diff --git a/src/emc/task/backtrace.cc b/src/emc/task/backtrace.cc index 1587fda7ab6..e031c98fc4b 100644 --- a/src/emc/task/backtrace.cc +++ b/src/emc/task/backtrace.cc @@ -21,6 +21,7 @@ #include #include #include +#include "config.h" extern int done; extern int emcOperatorError(const char *fmt, ...); @@ -34,7 +35,7 @@ void backtrace(int signo) signal(signo, SIG_IGN); // prevent multiple invocations on same signal snprintf(pid_buf, sizeof(pid_buf), "%d", getpid()); - snprintf(filename, sizeof(filename),"/tmp/backtrace.%s", pid_buf); + snprintf(filename, sizeof(filename), EMC2_TMP_DIR "/backtrace.%s", pid_buf); name_buf[readlink("/proc/self/exe", name_buf, 511)]=0; int child_pid = fork(); diff --git a/src/emc/task/signalhandler.cc b/src/emc/task/signalhandler.cc index b933ada5850..cc83d2f87f2 100644 --- a/src/emc/task/signalhandler.cc +++ b/src/emc/task/signalhandler.cc @@ -40,7 +40,7 @@ static void call_gdb(int sig, int start_gdb_in_window) FILE *f; char tmp_gdbrc[PATH_MAX]; - snprintf(tmp_gdbrc, sizeof(tmp_gdbrc), "/tmp/gdbrc.%d",getpid()); + snprintf(tmp_gdbrc, sizeof(tmp_gdbrc), EMC2_TMP_DIR "/gdbrc.%d",getpid()); if ((f = fopen(tmp_gdbrc,"w")) == NULL) { perror(tmp_gdbrc); diff --git a/src/emc/tooldata/tooldata_mmap.cc b/src/emc/tooldata/tooldata_mmap.cc index f9dacb417c2..e57783828b6 100644 --- a/src/emc/tooldata/tooldata_mmap.cc +++ b/src/emc/tooldata/tooldata_mmap.cc @@ -22,6 +22,7 @@ #include #include #include +#include "config.h" #include "rtapi_mutex.h" #include "tooldata.hh" @@ -73,7 +74,7 @@ typedef struct { static char* tool_mmap_fname(void) { if (*filename) {return filename;} char* hdir = secure_getenv("HOME"); - if (!hdir) {hdir = (char*)"/tmp";} + if (!hdir) { hdir = (char *) EMC2_TMP_DIR; } snprintf(filename,sizeof(filename),"%s/%s",hdir,TOOL_MMAP_FILENAME); return(filename); } diff --git a/src/hal/classicladder/files_project.c b/src/hal/classicladder/files_project.c index 2b921fe7701..d2256b327df 100644 --- a/src/hal/classicladder/files_project.c +++ b/src/hal/classicladder/files_project.c @@ -35,6 +35,7 @@ #include #endif #include "classicladder.h" +#include "config.h" #include "global.h" #include "edit.h" #include "calc.h" @@ -107,7 +108,7 @@ void InitTempDir( void ) { char * TmpEnv = getenv("TMP"); if ( TmpEnv==NULL ) - TmpEnv = "/tmp"; + TmpEnv = EMC2_TMP_DIR; // get a single name directory snprintf(TmpDirectory, sizeof(TmpDirectory), "%s/classicladder_tmp_XXXXXX", TmpEnv ); diff --git a/tcl/linuxcnc.tcl.in b/tcl/linuxcnc.tcl.in index 613568137d1..462d7f6a3ac 100644 --- a/tcl/linuxcnc.tcl.in +++ b/tcl/linuxcnc.tcl.in @@ -17,6 +17,7 @@ namespace eval linuxcnc { variable HOME @EMC2_HOME@ + variable TMP_DIR @EMC2_TMP_DIR@ variable BIN_DIR @EMC2_BIN_DIR@ variable TCL_DIR @EMC2_TCL_DIR@ variable TCL_LIB_DIR @EMC2_TCL_LIB_DIR@ From 387996b6662135acb316b7f2bc9128015b2e7545 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 16 Nov 2023 11:54:09 +0100 Subject: [PATCH 2/2] add support to open local gcode file from remote UI process When a process named != "emc" that has cms->ProcessType == CMS_REMOTE_TYPE, then * multiple EMC_TASK_PLAN_OPEN messages will be sent, each containing a chunk of the file's contents * each chunk will be stored in a temporary file on the host running milltask (process name == "emc") * the temporary file will be deleted upon EMC_TASK_PLAN_CLOSE --- src/emc/nml_intf/emc.cc | 8 ++- src/emc/nml_intf/emc_nml.hh | 7 ++ src/emc/task/emctaskmain.cc | 65 ++++++++++++++++- src/emc/usr_intf/axis/extensions/emcmodule.cc | 50 ++++++++++++++ src/emc/usr_intf/shcom.cc | 69 +++++++++++++++++-- 5 files changed, 188 insertions(+), 11 deletions(-) diff --git a/src/emc/nml_intf/emc.cc b/src/emc/nml_intf/emc.cc index c07071db37a..8d6c2c83cbb 100644 --- a/src/emc/nml_intf/emc.cc +++ b/src/emc/nml_intf/emc.cc @@ -1900,8 +1900,10 @@ void EMC_TASK_PLAN_OPEN::update(CMS * cms) { EMC_TASK_CMD_MSG::update(cms); - cms->update(file, 256); - + cms->update(file, LINELEN); + cms->update(remote_filesize); + cms->update(remote_buffersize); + cms->update(remote_buffer, sizeof(remote_buffer)); } /* @@ -2154,4 +2156,4 @@ void EMC_MOTION_ADAPTIVE::update(CMS * cms) void EMC_MOTION_CMD_MSG::update(CMS * cms) { -} \ No newline at end of file +} diff --git a/src/emc/nml_intf/emc_nml.hh b/src/emc/nml_intf/emc_nml.hh index c3a93e94ca0..14d1ea4a0b8 100644 --- a/src/emc/nml_intf/emc_nml.hh +++ b/src/emc/nml_intf/emc_nml.hh @@ -1012,7 +1012,14 @@ class EMC_TASK_PLAN_OPEN:public EMC_TASK_CMD_MSG { // For internal NML/CMS use only. void update(CMS * cms); + // (local) path to file char file[LINELEN]; + // total size of file in bytes (if issued from remote process, 0 otherwise) + size_t remote_filesize; + // amount of bytes currently in buffer (if issued from remote process, 0 otherwise) + size_t remote_buffersize; + // buffer used to transfer send a chunk of file contents (if loaded from remote process) + char remote_buffer[4096]; }; class EMC_TASK_PLAN_RUN:public EMC_TASK_CMD_MSG { diff --git a/src/emc/task/emctaskmain.cc b/src/emc/task/emctaskmain.cc index bd63e713767..a3a8ac69a5e 100644 --- a/src/emc/task/emctaskmain.cc +++ b/src/emc/task/emctaskmain.cc @@ -1630,6 +1630,7 @@ static int emcTaskIssueCommand(NMLmsg * cmd) { int retval = 0; int execRetval = 0; + static char remote_tmpfilename[LINELEN]; // path to temporary file received from remote process if (0 == cmd) { if (emc_debug & EMC_DEBUG_TASK_ISSUE) { @@ -2148,10 +2149,62 @@ static int emcTaskIssueCommand(NMLmsg * cmd) } else { retval = 0; } + /* delete temporary copy of remote file if it exists */ + if(remote_tmpfilename[0]) + unlink(remote_tmpfilename); break; case EMC_TASK_PLAN_OPEN_TYPE: - open_msg = (EMC_TASK_PLAN_OPEN *) cmd; + open_msg = (EMC_TASK_PLAN_OPEN *) cmd; + + /* receive file in chunks from remote process via open_msg->remote_buffer */ + if(open_msg->remote_filesize > 0) { + static size_t received; // amount of bytes of file received, yet + static int fd; // temporary file + + /* got empty chunk? */ + if(open_msg->remote_buffersize == 0) { + rcs_print_error("EMC_TASK_PLAN_OPEN received empty chunk from remote process.\n"); + retval = -1; + break; + } + + /* this chunk belongs to a new file? */ + if(received == 0) { + /* create new tempfile */ + snprintf(remote_tmpfilename, LINELEN, EMC2_TMP_DIR "/%s.XXXXXX", basename(open_msg->file)); + if((fd = mkstemp(remote_tmpfilename)) < 0) { + rcs_print_error("mkstemp(%s) error: %s", remote_tmpfilename, strerror(errno)); + retval = -1; + break; + } + } + + /* write chunk to tempfile */ + size_t bytes_written = write(fd, open_msg->remote_buffer, open_msg->remote_buffersize); + if(bytes_written < open_msg->remote_buffersize) { + rcs_print_error("fwrite(%s) error: %s", remote_tmpfilename, strerror(errno)); + retval = -1; + break; + } + /* record data written */ + received += bytes_written; + + /* not the last chunk? */ + if(received < open_msg->remote_filesize) { + /* we're done here for now */ + retval = 0; + break; + } + /* all chunks received - reset byte counter */ + received = 0; + /* close file */ + close(fd); + /* change filename to newly written local tmp file */ + rtapi_strxcpy(open_msg->file, remote_tmpfilename); + } + + /* open local file */ retval = emcTaskPlanOpen(open_msg->file); if (retval > INTERP_MIN_ERROR) { retval = -1; @@ -3275,6 +3328,16 @@ int main(int argc, char *argv[]) exit(1); } + // create EMC2_TMP_DIR if it's not existing, yet + struct stat s = {0}; + if (stat(EMC2_TMP_DIR, &s) != 0) { + if(mkdir(EMC2_TMP_DIR, 0700) != 0) { + rcs_print_error("mkdir(%s): %s", EMC2_TMP_DIR, strerror(errno)); + emctask_shutdown(); + exit(1); + } + } + // get our status data structure // moved up from emc_startup so we can expose it in Python right away emcStatus = new EMC_STAT; diff --git a/src/emc/usr_intf/axis/extensions/emcmodule.cc b/src/emc/usr_intf/axis/extensions/emcmodule.cc index b43f0411281..dcbc32f2295 100644 --- a/src/emc/usr_intf/axis/extensions/emcmodule.cc +++ b/src/emc/usr_intf/axis/extensions/emcmodule.cc @@ -1271,6 +1271,56 @@ static PyObject *program_open(pyCommandChannel *s, PyObject *o) { return NULL; } strcpy(m.file, file); + /* clear optional fields */ + m.remote_buffersize = 0; + m.remote_filesize = 0; + + /* send file in chunks to linuxcnc via remote_buffer for remote processes */ + if(s->s->cms->ProcessType == CMS_REMOTE_TYPE && strcmp(s->s->cms->ProcessName, "emc") != 0) { + /* open file */ + FILE *fd; + if(!(fd = fopen(file, "r"))) { + PyErr_Format(PyExc_OSError, "fopen(%s) error: %s", file, strerror(errno)); + return PyErr_SetFromErrno(PyExc_OSError); + } + /* get filesize */ + if(fseek(fd, 0L, SEEK_END) != 0) { + fclose(fd); + PyErr_Format(PyExc_OSError, "fseek(%s) error: %s", file, strerror(errno)); + return PyErr_SetFromErrno(PyExc_OSError); + } + if((m.remote_filesize = ftell(fd)) < 0) { + fclose(fd); + PyErr_Format(PyExc_OSError, "ftell(%s) error: %s", file, strerror(errno)); + return PyErr_SetFromErrno(PyExc_OSError); + } + if(fseek(fd, 0L, SEEK_SET) != 0) { + fclose(fd); + PyErr_Format(PyExc_OSError, "fseek(%s) error: %s", file, strerror(errno)); + return PyErr_SetFromErrno(PyExc_OSError); + } + + /* send complete file content in chunks of sizeof(msg.remote_buffer) */ + while(!(feof(fd))) { + size_t bytes_read = fread(&m.remote_buffer, 1, sizeof(m.remote_buffer), fd); + /* read error? */ + if(bytes_read <= 0 && ferror(fd)) { + PyErr_Format(PyExc_OSError, "fread(%s) error: %s", file, strerror(errno)); + return PyErr_SetFromErrno(PyExc_OSError); + } + /* save amount of bytes written to buffer */ + m.remote_buffersize = bytes_read; + /* send chunk */ + if(emcSendCommand(s, m) < 0) { + PyErr_Format(PyExc_OSError, "emcSendCommand() error: %s"); + return PyErr_SetFromErrno(PyExc_OSError); + } + } + fclose(fd); + Py_INCREF(Py_None); + return Py_None; + } + emcSendCommand(s, m); Py_INCREF(Py_None); return Py_None; diff --git a/src/emc/usr_intf/shcom.cc b/src/emc/usr_intf/shcom.cc index 659a8b150d9..eb0e363a04d 100644 --- a/src/emc/usr_intf/shcom.cc +++ b/src/emc/usr_intf/shcom.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -934,20 +935,74 @@ static char lastProgramFile[LINELEN] = ""; int sendProgramOpen(char *program) { - EMC_TASK_PLAN_OPEN emc_task_plan_open_msg; + int res = 0; + EMC_TASK_PLAN_OPEN msg; - // save this to run again + /* save this to run again */ rtapi_strxcpy(lastProgramFile, program); + /* store filename in message */ + rtapi_strxcpy(msg.file, program); + /* clear optional fields */ + msg.remote_buffersize = 0; + msg.remote_filesize = 0; + /* if we are a remote process, we send file in chunks to linuxcnc via remote_buffer */ + if(emcCommandBuffer->cms->ProcessType == CMS_REMOTE_TYPE && strcmp(emcCommandBuffer->cms->ProcessName, "emc") != 0) { + /* open file */ + FILE *fd; + if(!(fd = fopen(program, "r"))) { + rcs_print_error("fopen(%s) error: %s\n", program, strerror(errno)); + return -1; + } + /* get filesize */ + if(fseek(fd, 0L, SEEK_END) != 0) { + fclose(fd); + rcs_print_error("fseek(%s) error: %s\n", program, strerror(errno)); + return -1; + } + if((msg.remote_filesize = ftell(fd)) < 0) { + fclose(fd); + rcs_print_error("ftell(%s) error: %s\n", program, strerror(errno)); + return -1; + } + if(fseek(fd, 0L, SEEK_SET) != 0) { + fclose(fd); + rcs_print_error("fseek(%s) error: %s\n", program, strerror(errno)); + return -1; + } + + /* send complete file content in chunks of sizeof(msg.remote_buffer) */ + while(!(feof(fd))) { + size_t bytes_read = fread(&msg.remote_buffer, 1, sizeof(msg.remote_buffer), fd); + /* read error? */ + if(bytes_read <= 0 && ferror(fd)) { + rcs_print_error("fread(%s) error: %s\n", program, strerror(errno)); + res = -1; + break; + } + /* save amount of bytes written to buffer */ + msg.remote_buffersize = bytes_read; + /* send chunk */ + emcCommandSend(msg); + /* error happened? */ + if(emcCommandWaitDone() != 0) { + rcs_print_error("emcCommandSend() error\n"); + res = -1; + break; + } + } + fclose(fd); + return res; + } - rtapi_strxcpy(emc_task_plan_open_msg.file, program); - emcCommandSend(emc_task_plan_open_msg); + /* local process, just send filename */ + emcCommandSend(msg); if (emcWaitType == EMC_WAIT_RECEIVED) { - return emcCommandWaitReceived(); + return emcCommandWaitReceived(); } else if (emcWaitType == EMC_WAIT_DONE) { - return emcCommandWaitDone(); + return emcCommandWaitDone(); } - return 0; + return res; } int sendProgramRun(int line)