📄 efax_controller.cpp
字号:
/* Copyright (C) 2001 to 2004 Chris VineThis program is distributed under the General Public Licence, version 2.For particulars of this and relevant disclaimers see the fileCOPYING distributed with the source files.*/#include <unistd.h>#include <limits.h>#include <signal.h>#include <sys/stat.h>#include <sys/wait.h>#include <fcntl.h>#include <errno.h>#include <fstream>#include <iomanip>#include <ctime>#include <cstring>#include <cstdlib>// uncomment for debugging in EfaxController::timer_event()//#include <iostream>#include <glibmm/main.h>#include <glibmm/convert.h>#include <glibmm/timer.h>#include <glibmm/thread.h>#include "efax_controller.h"#ifdef HAVE_PTHREAD_SIGMASK #include <pthread.h>#endif#ifdef HAVE_STRINGSTREAM#include <sstream>#else#include <strstream>#endif#ifdef ENABLE_NLS#include <libintl.h>#endifEfaxController::EfaxController(void): child_pid(0), state(inactive), close_down(false) { // set up state_messages // it's OK to use std::string rather than Glib::ustring here, because // the contents are passed to StatusLine::write_status(const char*) and // std::string is just a transparent holder of the UTF-8 string // until it reaches that method state_messages.push_back(gettext("Inactive")); state_messages.push_back(gettext("Sending fax")); state_messages.push_back(gettext("Answering call")); state_messages.push_back(gettext("Answering call")); state_messages.push_back(gettext("Standing by to receive calls")); state_messages.push_back(gettext("Sending fax")); state_messages.push_back(gettext("Sending fax")); fax_made_notify.connect(sigc::mem_fun(*this, &EfaxController::sendfax_slot)); no_fax_made_notify.connect(sigc::mem_fun(*this, &EfaxController::no_fax_made_slot));}void EfaxController::efax_closedown(void) { if (state == inactive) ready_to_quit_notify(); else if (!close_down) { close_down = true; stop_slot(); }}void EfaxController::init_sendfax_parms(void) { sendfax_parms_vec = prog_config.parms; // now add the first set of arguments to the copy of prog_config.parms // in sendfax_parms_vec struct std::tm* time_p; std::time_t time_count; std::time(&time_count); time_p = std::localtime(&time_count); char date_string[23]; const char format[] = "%a %d-%b-%Y %H:%M"; std::strftime(date_string, 23, format, time_p); std::string temp("-h"); temp += date_string; temp += " "; try { temp += Glib::locale_from_utf8(gettext("From")); temp += " "; temp += Glib::locale_from_utf8(prog_config.my_name); } catch (Glib::ConvertError&) { write_error("UTF-8 conversion error in EfaxController::init_sendfax_parms()\n"); } temp += " ("; temp += prog_config.my_number + ") --> "; temp += last_fax_item_sent.number + " p.%d/%d"; sendfax_parms_vec.push_back(temp); if (last_fax_item_sent.number.empty()) { sendfax_parms_vec.push_back("-jX3"); sendfax_parms_vec.push_back("-t"); sendfax_parms_vec.push_back(""); } else { temp = "-t"; if (prog_config.tone_dial) temp += 'T'; else temp += 'P'; temp += last_fax_item_sent.number; sendfax_parms_vec.push_back(temp); }}void EfaxController::sendfax(const Fax_item& fax_item) { if (state == receive_standby) { if (!is_receiving_fax()) { last_fax_item_sent = fax_item; state = start_send_on_standby; kill_child(); } else write_error(gettext("Cannot send fax - a fax is being received\n")); } // note that this is in an else block - we do not want this // tested if state was set to start_send_on_standby above - we // want that test to occur on the call to this method in // EfaxController::timer_event() else if (state == inactive || state == start_send_on_standby) { stdout_pipe.open(Pipe_fifo::non_block); // efax is sensitive to timing, so set pipe write to non-block also stdout_pipe.make_write_non_block(); if (state == inactive) { state = sending; last_fax_item_sent = fax_item; } // else state == start_send_on_standby - we don't need to assign to // last_fax_item_sent in this case as it has already been done in the // 'if' block opening this function on the previous call to this function else state = send_on_standby; display_state(); // get the first set of arguments for the exec() call in sendfax_slot (because // this is a multi-threaded program, we must do this before fork()ing because // we use functions to get the arguments which are not async-signal-safe) // the arguments are loaded into the EfaxController object member sendfax_parms_vec init_sendfax_parms(); // now launch a worker thread to make up the fax pages in tiffg3 format // for sending by efax - the thread will emit dispatcher fax_made_notify // if the fax is correctly made up, which will call sendfax_slot() in this // initial (GUI) thread, which in turn invokes efax and sends the fax. If // the fax is not correctly made up, the worker thread will emit dispatcher // no_fax_made_notify, which will call no_fax_made_slot() in this initial // (GUI) thread so as to clean up correctly // first block off the signals for which we have set handlers so that the worker // thread does not receive the signals, otherwise we will have memory synchronisation // issues in multi-processor systems - we will unblock in the initial (GUI) thread // as soon as the worker thread has been launched sigset_t sig_mask; sigemptyset(&sig_mask); sigaddset(&sig_mask, SIGCHLD); sigaddset(&sig_mask, SIGQUIT); sigaddset(&sig_mask, SIGTERM); sigaddset(&sig_mask, SIGINT); sigaddset(&sig_mask, SIGHUP);#ifdef HAVE_PTHREAD_SIGMASK pthread_sigmask(SIG_BLOCK, &sig_mask, 0);#else sigprocmask(SIG_BLOCK, &sig_mask, 0);#endif try { Glib::Thread::create(sigc::mem_fun(*this, &EfaxController::make_fax_thread), false); } catch (Glib::ThreadError&) { write_error("Cannot start thread to make fax for sending\n"); stdout_pipe.close(); state = inactive; display_state(); } // now unblock signals so that the initial (GUI) thread can receive them#ifdef HAVE_PTHREAD_SIGMASK pthread_sigmask(SIG_UNBLOCK, &sig_mask, 0);#else sigprocmask(SIG_UNBLOCK, &sig_mask, 0);#endif } else beep();}void EfaxController::no_fax_made_slot(void) { cleanup_fax_send_fail(); stdout_pipe.close(); int state_val = state; state = inactive; if (state_val == send_on_standby) receive(receive_standby); // we don't need to call display_state() if we have called receive(), as // receive() will call display_state() itself else display_state();}std::pair<const char*, char* const*> EfaxController::get_sendfax_parms(void) { // sendfax_parms_vec has already been filled by init_sendfax_parms() and by // make_fax_thread() - so make up the C style arrays for the execvp() call // in sendfax_slot() char** exec_parms = new char*[sendfax_parms_vec.size() + 1]; std::vector<std::string>::const_iterator iter; char** temp_pp = exec_parms; for (iter = sendfax_parms_vec.begin(); iter != sendfax_parms_vec.end(); ++iter, ++temp_pp) { *temp_pp = new char[iter->size() + 1]; std::strcpy(*temp_pp, iter->c_str()); } *temp_pp = 0; char* prog_name = new char[std::strlen("efax-0.9a") + 1]; std::strcpy(prog_name, "efax-0.9a"); return std::pair<const char*, char* const*>(prog_name, exec_parms);}void EfaxController::sendfax_slot(void) { // this method runs in the initial (GUI) thread - we do this because // it exec()s a child process and we need to catch the SIGCHLD // signal when it finishes in the initial thread so that // efax_controller_childexit_handler() also only runs in the initial // (GUI) thread. That way, no memory synchronisation issues arise // on multi-processor systems with respect to the variables accessed // by EfaxController::timer_event(), which also runs in the initial // (GUI) thread. Linuxthreads will only allow the thread which fork()ed // to pick up SIGCHLD (its signal handling is deficient with respect to // the POSIX pthreads standard) thus requiring the use of Glib::Dispatcher // to "swap" execution threads at this point to complete sending of the // fax // we get this process to run in the initial (GUI) thread by invoking // it from a Glib::Dispatcher object in EfaxController::make_fax_thread(), // which will execute the slot attached to that object (this method) in // the thread in which the Glib::Dispatcher object was created. The // Glib::Dispatcher object was created as a member of the EfaxController // object, and thus was created in the initial (GUI) thread // get the arguments for the exec() call below (because this is a // multi-threaded program, we must do this before fork()ing because // we use functions to get the arguments which are not async-signal-safe) std::pair<const char*, char* const*> sendfax_parms(get_sendfax_parms()); // set up a synchronising pipe in case the child process finishes before // fork() in parent space has returned (yes, with an exec() error that can // happen with Linux kernel 2.6) - this is important because we test child_pid // in efax_controller_childexit_handler() Sync_pipe sync_pipe; child_pid = fork(); if (child_pid == -1) { write_error("Fork error - exiting\n"); std::exit(FORK_ERROR); } if (!child_pid) { // child process - as soon as everything is set up we are going to do an exec() // now we have forked, we can connect stdout_pipe to stdout // and connect MainWindow::error_pipe to stderr stdout_pipe.connect_to_stdout(); connect_to_stderr(); // wait before we call execvp() until the parent process has set itself up // and releases this process sync_pipe.wait(); execvp(sendfax_parms.first, sendfax_parms.second); // if we reached this point, then the execvp() call must have failed // report error and exit - uses _exit() and not exit() write_error("Can't find the efax-0.9a program - please check your installation\n" "and the PATH environmental variable\n"); _exit(EXEC_ERROR); } // end of child process // this is the parent process stdout_pipe.make_readonly(); // since the pipe is unidirectional, we can close the write fd join_child(); // now we have set up, release the child process sync_pipe.release(); // release the memory allocated on the heap for // the redundant sendfax_parms_pair // we are in the main parent process here - no worries about // only being able to use async-signal-safe functions delete_parms(sendfax_parms);}std::pair<const char*, char* const*> EfaxController::get_gs_parms(const std::string& basename) { // lock the Prog_config object to stop it being modified in the intial (GUI) thread // while we are accessing it here Glib::Mutex::Lock lock(*prog_config.mutex_p); std::vector<std::string> parms; std::string temp; parms.push_back("gs"); parms.push_back("-q"); parms.push_back("-sDEVICE=tiffg3"); temp = "-r"; temp += prog_config.resolution; parms.push_back(temp); parms.push_back("-dNOPAUSE"); parms.push_back("-dSAFER"); temp = "-sOutputFile="; temp += basename + ".%03d"; parms.push_back(temp); temp = "-sPAPERSIZE="; temp += prog_config.page_size; parms.push_back(temp); parms.push_back(basename); char** exec_parms = new char*[parms.size() + 1]; std::vector<std::string>::const_iterator iter; char** temp_pp = exec_parms; for (iter = parms.begin(); iter != parms.end(); ++iter, ++temp_pp) { *temp_pp = new char[iter->size() + 1]; std::strcpy(*temp_pp, iter->c_str()); } *temp_pp = 0; char* prog_name = new char[std::strlen("gs") + 1]; std::strcpy(prog_name, "gs"); return std::pair<const char*, char* const*>(prog_name, exec_parms);}void EfaxController::make_fax_thread(void) { // convert the postscript file(s) into tiffg3 fax files, beginning at [filename].001 // we will use ghostscript. we will also load the results into sendfax_parms_vec std::vector<std::string>::const_iterator filename_iter; // we do not need a mutex to protect last_fax_item_sent - until EfaxController::timer_event() // resets state to inactive or receive_standby, we cannot invoke sendfax() again for (filename_iter = last_fax_item_sent.file_list.begin(); filename_iter != last_fax_item_sent.file_list.end(); ++filename_iter) { std::string::size_type pos = filename_iter->find_last_of('/'); if (pos == std::string::npos || pos + 2 > filename_iter->size()) { try { write_error(Glib::locale_from_utf8(gettext("Not valid file name\n")).c_str()); } catch (Glib::ConvertError&) { write_error("UTF-8 conversion error in EfaxController::make_fax_thread()\n"); } no_fax_made_notify(); // clean up and then end this worker thread return; } else if (access(filename_iter->c_str(), F_OK)) { try { write_error(Glib::locale_from_utf8(gettext("File does not exist\n")).c_str()); } catch (Glib::ConvertError&) { write_error("UTF-8 conversion error in EfaxController::make_fax_thread()\n"); } no_fax_made_notify(); // clean up and then end this worker thread return; } else if (access(filename_iter->c_str(), R_OK)) { try { write_error(Glib::locale_from_utf8(gettext("User does not have read permission on the file\n")).c_str()); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -