Refactoring: Move all fork handling into a higher-order function
C++11 lambdas ftw.
This commit is contained in:
		
							parent
							
								
									1114c7bd57
								
							
						
					
					
						commit
						8e9140cfde
					
				
					 7 changed files with 128 additions and 206 deletions
				
			
		|  | @ -24,30 +24,14 @@ static std::pair<FdSink, FdSource> connect(const string & conn) | ||||||
|     Pipe to, from; |     Pipe to, from; | ||||||
|     to.create(); |     to.create(); | ||||||
|     from.create(); |     from.create(); | ||||||
|     pid_t child = fork(); |     startProcess([&]() { | ||||||
|     switch (child) { |         if (dup2(to.readSide, STDIN_FILENO) == -1) | ||||||
|         case -1: |             throw SysError("dupping stdin"); | ||||||
|             throw SysError("unable to fork"); |         if (dup2(from.writeSide, STDOUT_FILENO) == -1) | ||||||
|         case 0: |             throw SysError("dupping stdout"); | ||||||
|             try { |         execlp("ssh", "ssh", "-x", "-T", conn.c_str(), "nix-store --serve", NULL); | ||||||
|                 restoreAffinity(); |         throw SysError("executing ssh"); | ||||||
|                 if (dup2(to.readSide, STDIN_FILENO) == -1) |     }); | ||||||
|                     throw SysError("dupping stdin"); |  | ||||||
|                 if (dup2(from.writeSide, STDOUT_FILENO) == -1) |  | ||||||
|                     throw SysError("dupping stdout"); |  | ||||||
|                 execlp("ssh" |  | ||||||
|                       , "ssh" |  | ||||||
|                       , "-x" |  | ||||||
|                       , "-T" |  | ||||||
|                       , conn.c_str() |  | ||||||
|                       , "nix-store --serve" |  | ||||||
|                       , NULL); |  | ||||||
|                 throw SysError("executing ssh"); |  | ||||||
|             } catch (std::exception & e) { |  | ||||||
|                 std::cerr << "error: " << e.what() << std::endl; |  | ||||||
|             } |  | ||||||
|             _exit(1); |  | ||||||
|     } |  | ||||||
|     // If child exits unexpectedly, we'll EPIPE or EOF early.
 |     // If child exits unexpectedly, we'll EPIPE or EOF early.
 | ||||||
|     // If we exit unexpectedly, child will EPIPE or EOF early.
 |     // If we exit unexpectedly, child will EPIPE or EOF early.
 | ||||||
|     // So no need to keep track of it.
 |     // So no need to keep track of it.
 | ||||||
|  |  | ||||||
|  | @ -602,42 +602,29 @@ HookInstance::HookInstance() | ||||||
|     builderOut.create(); |     builderOut.create(); | ||||||
| 
 | 
 | ||||||
|     /* Fork the hook. */ |     /* Fork the hook. */ | ||||||
|     pid = fork(); |     pid = startProcess([&]() { | ||||||
|     switch (pid) { |  | ||||||
| 
 | 
 | ||||||
|     case -1: |         commonChildInit(fromHook); | ||||||
|         throw SysError("unable to fork"); |  | ||||||
| 
 | 
 | ||||||
|     case 0: |         if (chdir("/") == -1) throw SysError("changing into `/"); | ||||||
|         try { /* child */ |  | ||||||
| 
 | 
 | ||||||
|             commonChildInit(fromHook); |         /* Dup the communication pipes. */ | ||||||
|  |         if (dup2(toHook.readSide, STDIN_FILENO) == -1) | ||||||
|  |             throw SysError("dupping to-hook read side"); | ||||||
| 
 | 
 | ||||||
|             if (chdir("/") == -1) throw SysError("changing into `/"); |         /* Use fd 4 for the builder's stdout/stderr. */ | ||||||
|  |         if (dup2(builderOut.writeSide, 4) == -1) | ||||||
|  |             throw SysError("dupping builder's stdout/stderr"); | ||||||
| 
 | 
 | ||||||
|             /* Dup the communication pipes. */ |         execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(), | ||||||
|             if (dup2(toHook.readSide, STDIN_FILENO) == -1) |             (format("%1%") % settings.maxSilentTime).str().c_str(), | ||||||
|                 throw SysError("dupping to-hook read side"); |             (format("%1%") % settings.printBuildTrace).str().c_str(), | ||||||
|  |             (format("%1%") % settings.buildTimeout).str().c_str(), | ||||||
|  |             NULL); | ||||||
| 
 | 
 | ||||||
|             /* Use fd 4 for the builder's stdout/stderr. */ |         throw SysError(format("executing `%1%'") % buildHook); | ||||||
|             if (dup2(builderOut.writeSide, 4) == -1) |     }); | ||||||
|                 throw SysError("dupping builder's stdout/stderr"); |  | ||||||
| 
 | 
 | ||||||
|             execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(), |  | ||||||
|                 (format("%1%") % settings.maxSilentTime).str().c_str(), |  | ||||||
|                 (format("%1%") % settings.printBuildTrace).str().c_str(), |  | ||||||
|                 (format("%1%") % settings.buildTimeout).str().c_str(), |  | ||||||
|                 NULL); |  | ||||||
| 
 |  | ||||||
|             throw SysError(format("executing `%1%'") % buildHook); |  | ||||||
| 
 |  | ||||||
|         } catch (std::exception & e) { |  | ||||||
|             writeToStderr("build hook error: " + string(e.what()) + "\n"); |  | ||||||
|         } |  | ||||||
|         _exit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* parent */ |  | ||||||
|     pid.setSeparatePG(true); |     pid.setSeparatePG(true); | ||||||
|     pid.setKillSignal(SIGTERM); |     pid.setKillSignal(SIGTERM); | ||||||
|     fromHook.writeSide.close(); |     fromHook.writeSide.close(); | ||||||
|  | @ -2781,32 +2768,18 @@ void SubstitutionGoal::tryToRun() | ||||||
|     const char * * argArr = strings2CharPtrs(args); |     const char * * argArr = strings2CharPtrs(args); | ||||||
| 
 | 
 | ||||||
|     /* Fork the substitute program. */ |     /* Fork the substitute program. */ | ||||||
|     pid = fork(); |     pid = startProcess([&]() { | ||||||
| 
 | 
 | ||||||
|     switch (pid) { |         commonChildInit(logPipe); | ||||||
| 
 | 
 | ||||||
|     case -1: |         if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1) | ||||||
|         throw SysError("unable to fork"); |             throw SysError("cannot dup output pipe into stdout"); | ||||||
| 
 | 
 | ||||||
|     case 0: |         execv(sub.c_str(), (char * *) argArr); | ||||||
|         try { /* child */ |  | ||||||
| 
 | 
 | ||||||
|             commonChildInit(logPipe); |         throw SysError(format("executing `%1%'") % sub); | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|             if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1) |  | ||||||
|                 throw SysError("cannot dup output pipe into stdout"); |  | ||||||
| 
 |  | ||||||
|             execv(sub.c_str(), (char * *) argArr); |  | ||||||
| 
 |  | ||||||
|             throw SysError(format("executing `%1%'") % sub); |  | ||||||
| 
 |  | ||||||
|         } catch (std::exception & e) { |  | ||||||
|             writeToStderr("substitute error: " + string(e.what()) + "\n"); |  | ||||||
|         } |  | ||||||
|         _exit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* parent */ |  | ||||||
|     pid.setSeparatePG(true); |     pid.setSeparatePG(true); | ||||||
|     pid.setKillSignal(SIGTERM); |     pid.setKillSignal(SIGTERM); | ||||||
|     outPipe.writeSide.close(); |     outPipe.writeSide.close(); | ||||||
|  |  | ||||||
|  | @ -1083,31 +1083,16 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter & | ||||||
| 
 | 
 | ||||||
|     setSubstituterEnv(); |     setSubstituterEnv(); | ||||||
| 
 | 
 | ||||||
|     run.pid = fork(); |     run.pid = startProcess([&]() { | ||||||
| 
 |         if (dup2(toPipe.readSide, STDIN_FILENO) == -1) | ||||||
|     switch (run.pid) { |             throw SysError("dupping stdin"); | ||||||
| 
 |         if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1) | ||||||
|     case -1: |             throw SysError("dupping stdout"); | ||||||
|         throw SysError("unable to fork"); |         if (dup2(errorPipe.writeSide, STDERR_FILENO) == -1) | ||||||
| 
 |             throw SysError("dupping stderr"); | ||||||
|     case 0: /* child */ |         execl(substituter.c_str(), substituter.c_str(), "--query", NULL); | ||||||
|         try { |         throw SysError(format("executing `%1%'") % substituter); | ||||||
|             restoreAffinity(); |     }); | ||||||
|             if (dup2(toPipe.readSide, STDIN_FILENO) == -1) |  | ||||||
|                 throw SysError("dupping stdin"); |  | ||||||
|             if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1) |  | ||||||
|                 throw SysError("dupping stdout"); |  | ||||||
|             if (dup2(errorPipe.writeSide, STDERR_FILENO) == -1) |  | ||||||
|                 throw SysError("dupping stderr"); |  | ||||||
|             execl(substituter.c_str(), substituter.c_str(), "--query", NULL); |  | ||||||
|             throw SysError(format("executing `%1%'") % substituter); |  | ||||||
|         } catch (std::exception & e) { |  | ||||||
|             std::cerr << "error: " << e.what() << std::endl; |  | ||||||
|         } |  | ||||||
|         _exit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Parent. */ |  | ||||||
| 
 | 
 | ||||||
|     run.program = baseNameOf(substituter); |     run.program = baseNameOf(substituter); | ||||||
|     run.to = toPipe.writeSide.borrow(); |     run.to = toPipe.writeSide.borrow(); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,8 @@ | ||||||
| #include "config.h" | #include "config.h" | ||||||
| 
 | 
 | ||||||
|  | #include "util.hh" | ||||||
|  | #include "affinity.hh" | ||||||
|  | 
 | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <cerrno> | #include <cerrno> | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
|  | @ -16,8 +19,6 @@ | ||||||
| #include <sys/syscall.h> | #include <sys/syscall.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include "util.hh" |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| extern char * * environ; | extern char * * environ; | ||||||
| 
 | 
 | ||||||
|  | @ -714,6 +715,13 @@ Pid::Pid() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | Pid::Pid(pid_t pid) | ||||||
|  | { | ||||||
|  |     Pid(); | ||||||
|  |     *this = pid; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| Pid::~Pid() | Pid::~Pid() | ||||||
| { | { | ||||||
|     kill(); |     kill(); | ||||||
|  | @ -801,43 +809,30 @@ void killUser(uid_t uid) | ||||||
|        users to which the current process can send signals.  So we |        users to which the current process can send signals.  So we | ||||||
|        fork a process, switch to uid, and send a mass kill. */ |        fork a process, switch to uid, and send a mass kill. */ | ||||||
| 
 | 
 | ||||||
|     Pid pid; |     Pid pid = startProcess([&]() { | ||||||
|     pid = fork(); |  | ||||||
|     switch (pid) { |  | ||||||
| 
 | 
 | ||||||
|     case -1: |         if (setuid(uid) == -1) | ||||||
|         throw SysError("unable to fork"); |             throw SysError("setting uid"); | ||||||
| 
 | 
 | ||||||
|     case 0: |         while (true) { | ||||||
|         try { /* child */ |  | ||||||
| 
 |  | ||||||
|             if (setuid(uid) == -1) |  | ||||||
|                 throw SysError("setting uid"); |  | ||||||
| 
 |  | ||||||
|             while (true) { |  | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|                 /* OSX's kill syscall takes a third parameter that, among other
 |             /* OSX's kill syscall takes a third parameter that, among
 | ||||||
|                    things, determines if kill(-1, signo) affects the calling |                other things, determines if kill(-1, signo) affects the | ||||||
|                    process. In the OSX libc, it's set to true, which means |                calling process. In the OSX libc, it's set to true, | ||||||
|                    "follow POSIX", which we don't want here |                which means "follow POSIX", which we don't want here | ||||||
|                  */ |                  */ | ||||||
|                 if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break; |             if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break; | ||||||
| #else | #else | ||||||
|                 if (kill(-1, SIGKILL) == 0) break; |             if (kill(-1, SIGKILL) == 0) break; | ||||||
| #endif | #endif | ||||||
|                 if (errno == ESRCH) break; /* no more processes */ |             if (errno == ESRCH) break; /* no more processes */ | ||||||
|                 if (errno != EINTR) |             if (errno != EINTR) | ||||||
|                     throw SysError(format("cannot kill processes for uid `%1%'") % uid); |                 throw SysError(format("cannot kill processes for uid `%1%'") % uid); | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } catch (std::exception & e) { |  | ||||||
|             writeToStderr((format("killing processes belonging to uid `%1%': %2%\n") % uid % e.what()).str()); |  | ||||||
|             _exit(1); |  | ||||||
|         } |         } | ||||||
|         _exit(0); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     /* parent */ |         _exit(0); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     int status = pid.wait(true); |     int status = pid.wait(true); | ||||||
|     if (status != 0) |     if (status != 0) | ||||||
|         throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status)); |         throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status)); | ||||||
|  | @ -852,6 +847,25 @@ void killUser(uid_t uid) | ||||||
| //////////////////////////////////////////////////////////////////////
 | //////////////////////////////////////////////////////////////////////
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | pid_t startProcess(std::function<void()> fun, const string & errorPrefix) | ||||||
|  | { | ||||||
|  |     pid_t pid = fork(); | ||||||
|  |     if (pid == -1) throw SysError("unable to fork"); | ||||||
|  | 
 | ||||||
|  |     if (pid == 0) { | ||||||
|  |         try { | ||||||
|  |             restoreAffinity(); | ||||||
|  |             fun(); | ||||||
|  |         } catch (std::exception & e) { | ||||||
|  |             writeToStderr(errorPrefix + string(e.what()) + "\n"); | ||||||
|  |         } | ||||||
|  |         _exit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return pid; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| string runProgram(Path program, bool searchPath, const Strings & args) | string runProgram(Path program, bool searchPath, const Strings & args) | ||||||
| { | { | ||||||
|     checkInterrupt(); |     checkInterrupt(); | ||||||
|  | @ -867,32 +881,17 @@ string runProgram(Path program, bool searchPath, const Strings & args) | ||||||
|     pipe.create(); |     pipe.create(); | ||||||
| 
 | 
 | ||||||
|     /* Fork. */ |     /* Fork. */ | ||||||
|     Pid pid; |     Pid pid = startProcess([&]() { | ||||||
|     pid = fork(); |         if (dup2(pipe.writeSide, STDOUT_FILENO) == -1) | ||||||
|  |             throw SysError("dupping stdout"); | ||||||
| 
 | 
 | ||||||
|     switch (pid) { |         if (searchPath) | ||||||
|  |             execvp(program.c_str(), (char * *) &cargs[0]); | ||||||
|  |         else | ||||||
|  |             execv(program.c_str(), (char * *) &cargs[0]); | ||||||
| 
 | 
 | ||||||
|     case -1: |         throw SysError(format("executing `%1%'") % program); | ||||||
|         throw SysError("unable to fork"); |     }); | ||||||
| 
 |  | ||||||
|     case 0: /* child */ |  | ||||||
|         try { |  | ||||||
|             if (dup2(pipe.writeSide, STDOUT_FILENO) == -1) |  | ||||||
|                 throw SysError("dupping stdout"); |  | ||||||
| 
 |  | ||||||
|             if (searchPath) |  | ||||||
|                 execvp(program.c_str(), (char * *) &cargs[0]); |  | ||||||
|             else |  | ||||||
|                 execv(program.c_str(), (char * *) &cargs[0]); |  | ||||||
|             throw SysError(format("executing `%1%'") % program); |  | ||||||
| 
 |  | ||||||
|         } catch (std::exception & e) { |  | ||||||
|             writeToStderr("error: " + string(e.what()) + "\n"); |  | ||||||
|         } |  | ||||||
|         _exit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Parent. */ |  | ||||||
| 
 | 
 | ||||||
|     pipe.writeSide.close(); |     pipe.writeSide.close(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include <dirent.h> | #include <dirent.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <signal.h> | #include <signal.h> | ||||||
|  | #include <functional> | ||||||
| 
 | 
 | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
| 
 | 
 | ||||||
|  | @ -237,6 +238,7 @@ class Pid | ||||||
|     int killSignal; |     int killSignal; | ||||||
| public: | public: | ||||||
|     Pid(); |     Pid(); | ||||||
|  |     Pid(pid_t pid); | ||||||
|     ~Pid(); |     ~Pid(); | ||||||
|     void operator =(pid_t pid); |     void operator =(pid_t pid); | ||||||
|     operator pid_t(); |     operator pid_t(); | ||||||
|  | @ -252,6 +254,11 @@ public: | ||||||
| void killUser(uid_t uid); | void killUser(uid_t uid); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /* Fork a process that runs the given function, and return the child
 | ||||||
|  |    pid to the caller. */ | ||||||
|  | pid_t startProcess(std::function<void()> fun, const string & errorPrefix = "error: "); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Run a program and return its stdout in a string (i.e., like the
 | /* Run a program and return its stdout in a string (i.e., like the
 | ||||||
|    shell backtick operator). */ |    shell backtick operator). */ | ||||||
| string runProgram(Path program, bool searchPath = false, | string runProgram(Path program, bool searchPath = false, | ||||||
|  |  | ||||||
|  | @ -872,40 +872,27 @@ static void daemonLoop() | ||||||
|             printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid); |             printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid); | ||||||
| 
 | 
 | ||||||
|             /* Fork a child to handle the connection. */ |             /* Fork a child to handle the connection. */ | ||||||
|             pid_t child; |             startProcess([&]() { | ||||||
|             child = fork(); |                 /* Background the daemon. */ | ||||||
|  |                 if (setsid() == -1) | ||||||
|  |                     throw SysError(format("creating a new session")); | ||||||
| 
 | 
 | ||||||
|             switch (child) { |                 /* Restore normal handling of SIGCHLD. */ | ||||||
|  |                 setSigChldAction(false); | ||||||
| 
 | 
 | ||||||
|             case -1: |                 /* For debugging, stuff the pid into argv[1]. */ | ||||||
|                 throw SysError("unable to fork"); |                 if (clientPid != -1 && argvSaved[1]) { | ||||||
| 
 |                     string processName = int2String(clientPid); | ||||||
|             case 0: |                     strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1])); | ||||||
|                 try { /* child */ |  | ||||||
| 
 |  | ||||||
|                     /* Background the daemon. */ |  | ||||||
|                     if (setsid() == -1) |  | ||||||
|                         throw SysError(format("creating a new session")); |  | ||||||
| 
 |  | ||||||
|                     /* Restore normal handling of SIGCHLD. */ |  | ||||||
|                     setSigChldAction(false); |  | ||||||
| 
 |  | ||||||
|                     /* For debugging, stuff the pid into argv[1]. */ |  | ||||||
|                     if (clientPid != -1 && argvSaved[1]) { |  | ||||||
|                         string processName = int2String(clientPid); |  | ||||||
|                         strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1])); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     /* Handle the connection. */ |  | ||||||
|                     from.fd = remote; |  | ||||||
|                     to.fd = remote; |  | ||||||
|                     processConnection(trusted); |  | ||||||
| 
 |  | ||||||
|                 } catch (std::exception & e) { |  | ||||||
|                     writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n"); |  | ||||||
|                 } |                 } | ||||||
|                 exit(0); | 
 | ||||||
|             } |                 /* Handle the connection. */ | ||||||
|  |                 from.fd = remote; | ||||||
|  |                 to.fd = remote; | ||||||
|  |                 processConnection(trusted); | ||||||
|  | 
 | ||||||
|  |                 _exit(0); | ||||||
|  |             }, "unexpected Nix daemon error: "); | ||||||
| 
 | 
 | ||||||
|         } catch (Interrupted & e) { |         } catch (Interrupted & e) { | ||||||
|             throw; |             throw; | ||||||
|  |  | ||||||
|  | @ -939,27 +939,14 @@ static void opServe(Strings opFlags, Strings opArgs) | ||||||
|                     Pipe fromDecompressor; |                     Pipe fromDecompressor; | ||||||
|                     fromDecompressor.create(); |                     fromDecompressor.create(); | ||||||
| 
 | 
 | ||||||
|                     Pid pid; |                     Pid pid = startProcess([&]() { | ||||||
|                     pid = fork(); |                         fromDecompressor.readSide.close(); | ||||||
| 
 |                         if (dup2(fromDecompressor.writeSide, STDOUT_FILENO) == -1) | ||||||
|                     switch (pid) { |                             throw SysError("dupping stdout"); | ||||||
| 
 |                         // FIXME: use absolute path.
 | ||||||
|                         case -1: |                         execlp(compression.c_str(), compression.c_str(), "-d", NULL); | ||||||
|                             throw SysError("unable to fork"); |                         throw SysError(format("executing `%1%'") % compression); | ||||||
| 
 |                     }); | ||||||
|                         case 0: /* child */ |  | ||||||
|                             try { |  | ||||||
|                                 fromDecompressor.readSide.close(); |  | ||||||
|                                 if (dup2(fromDecompressor.writeSide, STDOUT_FILENO) == -1) |  | ||||||
|                                     throw SysError("dupping stdout"); |  | ||||||
|                                 // FIXME: use absolute path.
 |  | ||||||
|                                 execlp(compression.c_str(), compression.c_str(), "-d", NULL); |  | ||||||
|                                 throw SysError(format("executing `%1%'") % compression); |  | ||||||
|                             } catch (std::exception & e) { |  | ||||||
|                                 std::cerr << "error: " << e.what() << std::endl; |  | ||||||
|                             } |  | ||||||
|                             _exit(1); |  | ||||||
|                     } |  | ||||||
| 
 | 
 | ||||||
|                     fromDecompressor.writeSide.close(); |                     fromDecompressor.writeSide.close(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue