1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 24 июл. 2019 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <core/debug.h>
23 #include <core/ipc/Process.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <data/cstorage.h>
28 #include <time.h>
29 #include <core/io/OutFileStream.h>
30 #include <core/io/InFileStream.h>
31 
32 #if defined(PLATFORM_WINDOWS)
33     #include <processthreadsapi.h>
34     #include <namedpipeapi.h>
35     #include <synchapi.h>
36     #include <processenv.h>
37 #else
38     #include <spawn.h>
39     #include <sys/wait.h>
40 
41     #ifndef _GNU_SOURCE
42         extern char **environ;    // Define environment variables
43     #endif /* _GNU_SOURCE */
44 #endif /* PLATFORM_WINDOWS */
45 
46 namespace lsp
47 {
48     namespace ipc
49     {
50 
Process()51         Process::Process()
52         {
53             nStatus             = PSTATUS_CREATED;
54             nExitCode           = 0;
55 
56 #ifdef PLATFORM_WINDOWS
57             hProcess            = NULL;
58             nPID                = 0;
59             hStdIn              = NULL;
60             hStdOut             = NULL;
61             hStdErr             = NULL;
62 #else
63             nPID                = 0;
64             hStdIn              = -1;
65             hStdOut             = -1;
66             hStdErr             = -1;
67 #endif
68 
69             pStdIn              = NULL;
70             pStdOut             = NULL;
71             pStdErr             = NULL;
72 
73             if (copy_env() != STATUS_OK)
74                 nStatus             = PSTATUS_ERROR;
75         }
76 
~Process()77         Process::~Process()
78         {
79             destroy_args(&vArgs);
80             destroy_env(&vEnv);
81             close_handles();
82 
83             if (pStdIn != NULL)
84             {
85                 pStdIn->close();
86                 delete pStdIn;
87                 pStdIn  = NULL;
88             }
89 
90             if (pStdOut != NULL)
91             {
92                 pStdOut->close();
93                 delete pStdOut;
94                 pStdOut  = NULL;
95             }
96 
97             if (pStdErr != NULL)
98             {
99                 pStdErr->close();
100                 delete pStdErr;
101                 pStdErr  = NULL;
102             }
103 
104 #ifdef PLATFORM_WINDOWS
105             if (hProcess != NULL)
106             {
107                 ::CloseHandle(hProcess);
108                 hProcess    = NULL;
109             }
110 #endif /* PLATFORM_WINDOWS */
111         }
112 
destroy_args(cvector<LSPString> * args)113         void Process::destroy_args(cvector<LSPString> *args)
114         {
115             for (size_t i=0, n=args->size(); i<n; ++i)
116             {
117                 LSPString *arg = args->at(i);
118                 delete arg;
119             }
120             args->flush();
121         }
122 
destroy_env(cvector<envvar_t> * env)123         void Process::destroy_env(cvector<envvar_t> *env)
124         {
125             for (size_t i=0, n=env->size(); i<n; ++i)
126             {
127                 envvar_t *var= env->at(i);
128                 delete var;
129             }
130             env->flush();
131         }
132 
set_command(const LSPString * cmd)133         status_t Process::set_command(const LSPString *cmd)
134         {
135             if (nStatus != PSTATUS_CREATED)
136                 return STATUS_BAD_STATE;
137 
138             if (cmd == NULL)
139             {
140                 sCommand.clear();
141                 return STATUS_OK;
142             }
143 
144             return (sCommand.set(cmd)) ? STATUS_OK : STATUS_NO_MEM;
145         }
146 
set_command(const char * cmd)147         status_t Process::set_command(const char *cmd)
148         {
149             if (nStatus != PSTATUS_CREATED)
150                 return STATUS_BAD_STATE;
151 
152             if (cmd == NULL)
153             {
154                 sCommand.clear();
155                 return STATUS_OK;
156             }
157 
158             return (sCommand.set_utf8(cmd)) ? STATUS_OK : STATUS_NO_MEM;
159         }
160 
args() const161         size_t Process::args() const
162         {
163             return vArgs.size();
164         }
165 
add_arg(const LSPString * value)166         status_t Process::add_arg(const LSPString *value)
167         {
168             if (value == NULL)
169                 return STATUS_BAD_ARGUMENTS;
170             if (nStatus != PSTATUS_CREATED)
171                 return STATUS_BAD_STATE;
172 
173             LSPString *arg = new LSPString();
174             if (arg == NULL)
175                 return STATUS_NO_MEM;
176 
177             if (!arg->set(value))
178             {
179                 delete arg;
180                 return STATUS_NO_MEM;
181             }
182             if (!vArgs.add(arg))
183             {
184                 delete arg;
185                 return STATUS_NO_MEM;
186             }
187 
188             return STATUS_OK;
189         }
190 
add_arg(const char * value)191         status_t Process::add_arg(const char *value)
192         {
193             if (value == NULL)
194                 return STATUS_BAD_ARGUMENTS;
195             if (nStatus != PSTATUS_CREATED)
196                 return STATUS_BAD_STATE;
197 
198             LSPString *arg = new LSPString();
199             if (arg == NULL)
200                 return STATUS_NO_MEM;
201 
202             if (!arg->set_utf8(value))
203             {
204                 delete arg;
205                 return STATUS_NO_MEM;
206             }
207             if (!vArgs.add(arg))
208             {
209                 delete arg;
210                 return STATUS_NO_MEM;
211             }
212 
213             return STATUS_OK;
214         }
215 
set_arg(size_t index,const LSPString * value)216         status_t Process::set_arg(size_t index, const LSPString *value)
217         {
218             if (value == NULL)
219                 return STATUS_BAD_ARGUMENTS;
220             if (nStatus != PSTATUS_CREATED)
221                 return STATUS_BAD_STATE;
222 
223             LSPString *ptr = vArgs.get(index);
224             if (ptr == NULL)
225                 return STATUS_BAD_ARGUMENTS;
226             return (ptr->set(value)) ? STATUS_OK : STATUS_NO_MEM;
227         }
228 
set_arg(size_t index,const char * value)229         status_t Process::set_arg(size_t index, const char *value)
230         {
231             if (value == NULL)
232                 return STATUS_BAD_ARGUMENTS;
233             if (nStatus != PSTATUS_CREATED)
234                 return STATUS_BAD_STATE;
235 
236             LSPString *ptr = vArgs.get(index);
237             if (ptr == NULL)
238                 return STATUS_BAD_ARGUMENTS;
239             return (ptr->set_utf8(value)) ? STATUS_OK : STATUS_NO_MEM;
240         }
241 
get_arg(size_t index,LSPString * value)242         status_t Process::get_arg(size_t index, LSPString *value)
243         {
244             LSPString *ptr = vArgs.get(index);
245             if (ptr == NULL)
246                 return STATUS_BAD_ARGUMENTS;
247             if (value == NULL)
248                 return STATUS_OK;
249             return (value->set(ptr)) ? STATUS_OK : STATUS_NO_MEM;
250         }
251 
get_arg(size_t index,char ** value)252         status_t Process::get_arg(size_t index, char **value)
253         {
254             LSPString *ptr = vArgs.get(index);
255             if (ptr == NULL)
256                 return STATUS_BAD_ARGUMENTS;
257             if (value == NULL)
258                 return STATUS_OK;
259 
260             char *dup = ptr->clone_utf8();
261             if (dup == NULL)
262                 return STATUS_NO_MEM;
263 
264             *value      = dup;
265             return STATUS_OK;
266         }
267 
remove_arg(size_t index,LSPString * value)268         status_t Process::remove_arg(size_t index, LSPString *value)
269         {
270             if (nStatus != PSTATUS_CREATED)
271                 return STATUS_BAD_STATE;
272 
273             LSPString *ptr = vArgs.get(index);
274             if (ptr == NULL)
275                 return STATUS_BAD_ARGUMENTS;
276             if (value != NULL)
277                 value->swap(ptr);
278 
279             vArgs.remove(index);
280             delete ptr;
281             return STATUS_OK;
282         }
283 
remove_arg(size_t index,char ** value)284         status_t Process::remove_arg(size_t index, char **value)
285         {
286             if (nStatus != PSTATUS_CREATED)
287                 return STATUS_BAD_STATE;
288 
289             LSPString *ptr = vArgs.get(index);
290             if (ptr == NULL)
291                 return STATUS_BAD_ARGUMENTS;
292             if (value != NULL)
293             {
294                 char *dup = ptr->clone_utf8();
295                 if (dup == NULL)
296                     return STATUS_NO_MEM;
297 
298                 *value = dup;
299             }
300 
301             vArgs.remove(index);
302             delete ptr;
303             return STATUS_OK;
304         }
305 
insert_arg(size_t index,const LSPString * value)306         status_t Process::insert_arg(size_t index, const LSPString *value)
307         {
308             if (nStatus != PSTATUS_CREATED)
309                 return STATUS_BAD_STATE;
310             if (value == NULL)
311                 return STATUS_BAD_ARGUMENTS;
312 
313             LSPString *arg = new LSPString();
314             if (arg == NULL)
315                 return STATUS_NO_MEM;
316 
317             if (!arg->set(value))
318             {
319                 delete arg;
320                 return STATUS_NO_MEM;
321             }
322             if (!vArgs.insert(arg, index))
323             {
324                 delete arg;
325                 return STATUS_NO_MEM;
326             }
327 
328             return STATUS_OK;
329         }
330 
insert_arg(size_t index,const char * value)331         status_t Process::insert_arg(size_t index, const char *value)
332         {
333             if (nStatus != PSTATUS_CREATED)
334                 return STATUS_BAD_STATE;
335             if (value == NULL)
336                 return STATUS_BAD_ARGUMENTS;
337 
338             LSPString *arg = new LSPString();
339             if (arg == NULL)
340                 return STATUS_NO_MEM;
341 
342             if (!arg->set_utf8(value))
343             {
344                 delete arg;
345                 return STATUS_NO_MEM;
346             }
347             if (!vArgs.insert(arg, index))
348             {
349                 delete arg;
350                 return STATUS_NO_MEM;
351             }
352 
353             return STATUS_OK;
354         }
355 
clear_args()356         status_t Process::clear_args()
357         {
358             if (nStatus != PSTATUS_CREATED)
359                 return STATUS_BAD_STATE;
360             destroy_args(&vArgs);
361             return STATUS_OK;
362         }
363 
envs() const364         size_t Process::envs() const
365         {
366             return vEnv.size();
367         }
368 
set_env(const LSPString * key,const LSPString * value)369         status_t Process::set_env(const LSPString *key, const LSPString *value)
370         {
371             if (nStatus != PSTATUS_CREATED)
372                 return STATUS_BAD_STATE;
373             if ((key == NULL) || (value == NULL))
374                 return STATUS_BAD_ARGUMENTS;
375             if (key->index_of('=') >= 0)
376                 return STATUS_BAD_FORMAT;
377 
378             envvar_t *var;
379             for (size_t i=0, n=vEnv.size(); i<n; ++i)
380             {
381                 var = vEnv.at(i);
382                 if (var->name.equals(key))
383                     return (var->value.set(value)) ? STATUS_OK : STATUS_NO_MEM;
384             }
385 
386             if ((var = new envvar_t) == NULL)
387                 return STATUS_NO_MEM;
388 
389             if ((!var->name.set(key)) || (!var->value.set(value)))
390             {
391                 delete var;
392                 return STATUS_NO_MEM;
393             }
394             if (!vEnv.add(var))
395             {
396                 delete var;
397                 return STATUS_NO_MEM;
398             }
399             return STATUS_OK;
400         }
401 
set_env(const char * key,const char * value)402         status_t Process::set_env(const char *key, const char *value)
403         {
404             if (nStatus != PSTATUS_CREATED)
405                 return STATUS_BAD_STATE;
406             if ((key == NULL) || (value == NULL))
407                 return STATUS_BAD_ARGUMENTS;
408             if (strchr(key, '=') != NULL)
409                 return STATUS_BAD_FORMAT;
410 
411             LSPString k, v;
412             if ((!k.set_utf8(key)) || (!v.set_utf8(value)))
413                 return STATUS_NO_MEM;
414             return set_env(&k, &v);
415         }
416 
remove_env(const LSPString * key,LSPString * value)417         status_t Process::remove_env(const LSPString *key, LSPString *value)
418         {
419             if (nStatus != PSTATUS_CREATED)
420                 return STATUS_BAD_STATE;
421             if (key == NULL)
422                 return STATUS_BAD_ARGUMENTS;
423 
424             for (size_t i=0, n=vEnv.size(); i<n; ++i)
425             {
426                 envvar_t *var = vEnv.at(i);
427                 if (var->name.equals(key))
428                 {
429                     if (value != NULL)
430                         value->swap(&var->value);
431                     delete var;
432                     vEnv.remove(i, true);
433                     return STATUS_OK;
434                 }
435             }
436 
437             return STATUS_NOT_FOUND;
438         }
439 
remove_env(const char * key,char ** value)440         status_t Process::remove_env(const char *key, char **value)
441         {
442             if (nStatus != PSTATUS_CREATED)
443                 return STATUS_BAD_STATE;
444             if (key == NULL)
445                 return STATUS_BAD_ARGUMENTS;
446 
447             LSPString k;
448             if (!k.set_utf8(key))
449                 return STATUS_NO_MEM;
450 
451             for (size_t i=0, n=vEnv.size(); i<n; ++i)
452             {
453                 envvar_t *var = vEnv.at(i);
454                 if (var->name.equals(&k))
455                 {
456                     if (value != NULL)
457                     {
458                         char *dup       = var->value.clone_utf8();
459                         if (dup == NULL)
460                             return STATUS_NO_MEM;
461                         *value          = dup;
462                     }
463                     delete var;
464                     vEnv.remove(i, true);
465                     return STATUS_OK;
466                 }
467             }
468 
469             return STATUS_NOT_FOUND;
470         }
471 
remove_env(const char * key,LSPString * value)472         status_t Process::remove_env(const char *key, LSPString *value)
473         {
474             if (nStatus != PSTATUS_CREATED)
475                 return STATUS_BAD_STATE;
476             if (key == NULL)
477                 return STATUS_BAD_ARGUMENTS;
478 
479             LSPString k;
480             if (!k.set_utf8(key))
481                 return STATUS_NO_MEM;
482 
483             for (size_t i=0, n=vEnv.size(); i<n; ++i)
484             {
485                 envvar_t *var = vEnv.at(i);
486                 if (!var->name.equals(&k))
487                     continue;
488 
489                 if (value != NULL)
490                     value->swap(&var->value);
491                 delete var;
492                 vEnv.remove(i, true);
493                 return STATUS_OK;
494             }
495 
496             return STATUS_NOT_FOUND;
497         }
498 
get_env(const LSPString * key,LSPString * value)499         status_t Process::get_env(const LSPString *key, LSPString *value)
500         {
501             if (key == NULL)
502                 return STATUS_BAD_ARGUMENTS;
503 
504             if (nStatus != PSTATUS_CREATED)
505                 return STATUS_BAD_STATE;
506             if (key == NULL)
507                 return STATUS_BAD_ARGUMENTS;
508 
509             for (size_t i=0, n=vEnv.size(); i<n; ++i)
510             {
511                 envvar_t *var = vEnv.at(i);
512                 if (!var->name.equals(key))
513                     continue;
514 
515                 if (value != NULL)
516                 {
517                     if (!value->set(&var->value))
518                         return STATUS_NO_MEM;
519                 }
520                 return STATUS_OK;
521             }
522 
523             return STATUS_NOT_FOUND;
524         }
525 
get_env(const char * key,LSPString * value)526         status_t Process::get_env(const char *key, LSPString *value)
527         {
528             if (key == NULL)
529                 return STATUS_BAD_ARGUMENTS;
530 
531             LSPString k;
532             if (!k.set_utf8(key))
533                 return STATUS_NO_MEM;
534 
535             for (size_t i=0, n=vEnv.size(); i<n; ++i)
536             {
537                 envvar_t *var = vEnv.at(i);
538                 if (!var->name.equals(&k))
539                     continue;
540 
541                 if (value != NULL)
542                 {
543                     if (!value->set(&var->value))
544                         return STATUS_NO_MEM;
545                 }
546                 return STATUS_OK;
547             }
548 
549             return STATUS_NOT_FOUND;
550         }
551 
get_env(const char * key,char ** value)552         status_t Process::get_env(const char *key, char **value)
553         {
554             if (key == NULL)
555                 return STATUS_BAD_ARGUMENTS;
556 
557             LSPString k;
558             if (!k.set_utf8(key))
559                 return STATUS_NO_MEM;
560 
561             for (size_t i=0, n=vEnv.size(); i<n; ++i)
562             {
563                 envvar_t *var = vEnv.at(i);
564                 if (!var->name.equals(&k))
565                     continue;
566 
567                 if (value != NULL)
568                 {
569                     char *dup       = var->value.clone_utf8();
570                     if (dup == NULL)
571                         return STATUS_NO_MEM;
572                     *value          = dup;
573                 }
574                 return STATUS_OK;
575             }
576 
577             return STATUS_NOT_FOUND;
578         }
579 
read_env(size_t idx,LSPString * key,LSPString * value)580         status_t Process::read_env(size_t idx, LSPString *key, LSPString *value)
581         {
582             if ((key == NULL) && (value == NULL))
583                 return STATUS_BAD_ARGUMENTS;
584 
585             envvar_t *var = vEnv.get(idx);
586             if (var == NULL)
587                 return STATUS_BAD_ARGUMENTS;
588 
589             LSPString tk, tv;
590             if (key != NULL)
591             {
592                 if (!tk.set(&var->name))
593                     return STATUS_NO_MEM;
594                 if (value != NULL)
595                 {
596                     if (!tv.set(&var->value))
597                         return STATUS_NO_MEM;
598                     value->swap(&tv);
599                 }
600 
601                 key->swap(&tk);
602                 return STATUS_OK;
603             }
604 
605             if (!tv.set(&var->value))
606                 return STATUS_NO_MEM;
607 
608             value->swap(&tv);
609             return STATUS_OK;
610         }
611 
read_env(size_t idx,char ** key,char ** value)612         status_t Process::read_env(size_t idx, char **key, char **value)
613         {
614             if ((key == NULL) && (value == NULL))
615                 return STATUS_BAD_ARGUMENTS;
616 
617             envvar_t *var = vEnv.get(idx);
618             if (var == NULL)
619                 return STATUS_BAD_ARGUMENTS;
620 
621             char *tk, *tv;
622 
623             if (key != NULL)
624             {
625                 if ((tk = var->name.clone_utf8()) == NULL)
626                     return STATUS_NO_MEM;
627 
628                 if (value != NULL)
629                 {
630                     if ((tv = var->value.clone_utf8()) == NULL)
631                     {
632                         ::free(tk);
633                         return STATUS_NO_MEM;
634                     }
635 
636                     *value = tv;
637                 }
638 
639                 *key = tk;
640                 return STATUS_OK;
641             }
642 
643             if ((tv = var->value.clone_utf8()) == NULL)
644                 return STATUS_NO_MEM;
645 
646             *value = tv;
647             return STATUS_OK;
648         }
649 
clear_env()650         status_t Process::clear_env()
651         {
652             if (nStatus != PSTATUS_CREATED)
653                 return STATUS_BAD_STATE;
654             destroy_env(&vEnv);
655             return STATUS_OK;
656         }
657 
status()658         size_t Process::status()
659         {
660             wait(0); // Try to update status
661             return nStatus;
662         }
663 
exited()664         bool Process::exited()
665         {
666             return status() == PSTATUS_EXITED;
667         }
668 
running()669         bool Process::running()
670         {
671             return status() == PSTATUS_RUNNING;
672         }
673 
valid()674         bool Process::valid()
675         {
676             return status() != PSTATUS_ERROR;
677         }
678 
process_id() const679         ssize_t Process::process_id() const
680         {
681             switch (nStatus)
682             {
683                 case PSTATUS_RUNNING:
684                 case PSTATUS_EXITED:
685                     return nPID;
686                 default:
687                     break;
688             }
689             return -1;
690         }
691 
exit_code(int * code)692         status_t Process::exit_code(int *code)
693         {
694             if (code == NULL)
695                 return STATUS_BAD_ARGUMENTS;
696 
697             if (nStatus == PSTATUS_CREATED)
698                 return STATUS_BAD_STATE;
699             if (nStatus == PSTATUS_RUNNING)
700             {
701                 status_t res = wait(0);
702                 if (res != STATUS_OK)
703                     return STATUS_BAD_STATE;
704             }
705 
706             *code   = nExitCode;
707             return STATUS_OK;
708         }
709 
710 #ifdef PLATFORM_WINDOWS
append_arg_escaped(LSPString * dst,const LSPString * value)711         status_t Process::append_arg_escaped(LSPString *dst, const LSPString *value)
712         {
713             if (value->is_empty())
714                 return (dst->append_ascii("\"\"")) ? STATUS_OK : STATUS_NO_MEM;
715 
716             for (size_t i=0, n=value->length(); i<n; ++i)
717             {
718                 lsp_wchar_t ch = value->char_at(i);
719                 switch (ch)
720                 {
721                     case ' ':
722                         if (!dst->append_ascii("\" \"", 3))
723                             return STATUS_NO_MEM;
724                         break;
725                     case '"':
726                         if (!dst->append_ascii("\\\"", 2))
727                             return STATUS_NO_MEM;
728                         break;
729                     default:
730                         if (!dst->append(ch))
731                             return STATUS_NO_MEM;
732                         break;
733                 }
734             }
735 
736             return STATUS_OK;
737         }
738 
build_argv(LSPString * dst)739         status_t Process::build_argv(LSPString *dst)
740         {
741             status_t res = append_arg_escaped(dst, &sCommand);
742             if (res != STATUS_OK)
743                 return res;
744 
745             for (size_t i=0, n=vArgs.size(); i<n; ++i)
746             {
747                 LSPString *arg = vArgs.at(i);
748                 if (!dst->append(' '))
749                     return STATUS_NO_MEM;
750                 res = append_arg_escaped(dst, arg);
751                 if (res != STATUS_OK)
752                     return res;
753             }
754 
755             return STATUS_OK;
756         }
757 
build_envp(LSPString * dst)758         status_t Process::build_envp(LSPString *dst)
759         {
760             for (size_t i=0, n=vEnv.size(); i<n; ++i)
761             {
762                 envvar_t *env = vEnv.at(i);
763                 if (!dst->append(&env->name))
764                     return STATUS_NO_MEM;
765                 if (!dst->append('='))
766                     return STATUS_NO_MEM;
767                 if (!dst->append(&env->value))
768                     return STATUS_NO_MEM;
769                 if (!dst->append('\0'))
770                     return STATUS_NO_MEM;
771             }
772 
773             // Note that an ANSI environment block is terminated by two zero bytes:
774             // one for the last string, one more to terminate the block. A Unicode
775             // environment block is terminated by four zero bytes: two for the last
776             // string, two more to terminate the block.
777             return (dst->append('\0')) ? STATUS_OK : STATUS_NO_MEM;
778         }
779 
launch()780         status_t Process::launch()
781         {
782             if (nStatus != PSTATUS_CREATED)
783                 return STATUS_BAD_STATE;
784             if (sCommand.is_empty())
785                 return STATUS_BAD_STATE;
786 
787             // Form argv
788             LSPString argv;
789             status_t res = build_argv(&argv);
790             if (res != STATUS_OK)
791                 return res;
792 
793             // Form envp
794             LSPString envp;
795             res = build_envp(&envp);
796             if (res != STATUS_OK)
797                 return res;
798 
799             // Launch child process
800             STARTUPINFOW si;
801             PROCESS_INFORMATION pi;
802 
803             ZeroMemory( &si, sizeof(STARTUPINFOW) );
804             ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
805             si.cb       = sizeof(si);
806 
807             // Override STDIN, STDOUT, STDERR
808             if (hStdIn != NULL)
809             {
810                 si.dwFlags     |= STARTF_USESTDHANDLES;
811                 si.hStdInput    = hStdIn;
812             }
813             if (hStdOut != NULL)
814             {
815                 si.dwFlags     |= STARTF_USESTDHANDLES;
816                 si.hStdOutput   = hStdOut;
817             }
818             if (hStdErr != NULL)
819             {
820                 si.dwFlags     |= STARTF_USESTDHANDLES;
821                 si.hStdError    = hStdErr;
822             }
823 
824             // Prepare buffers
825             WCHAR *wargv        = argv.clone_utf16();
826             if (wargv == NULL)
827                 return STATUS_NO_MEM;
828 
829             WCHAR *wenvp        = envp.clone_utf16();
830             if (wenvp == NULL)
831             {
832                 ::free(wargv);
833                 return STATUS_NO_MEM;
834             }
835 
836             // Start the child process.
837             if( !::CreateProcessW(
838                 sCommand.get_utf16(),   // Module name (use command line)
839                 wargv,                  // Command line
840                 NULL,                   // Process handle not inheritable
841                 NULL,                   // Thread handle not inheritable
842                 FALSE,                  // Set handle inheritance to FALSE
843                 CREATE_UNICODE_ENVIRONMENT, // Use unicode environment
844                 wenvp,                  // Set-up environment block
845                 NULL,                   // Use parent's starting directory
846                 &si,                    // Pointer to STARTUPINFO structure
847                 &pi                     // Pointer to PROCESS_INFORMATION structure
848             ))
849             {
850                 int error = ::GetLastError();
851                 ::fprintf(stderr, "Failed to create child process (%d)\n", error);
852                 ::fflush(stderr);
853 
854                 ::free(wenvp);
855                 ::free(wargv);
856 
857                 return STATUS_UNKNOWN_ERR;
858             }
859 
860             // Free resources and close redirected file handles
861             ::free(wenvp);
862             ::free(wargv);
863             close_handles();
864 
865             // Update state
866             hProcess    = pi.hProcess;
867             nPID        = pi.dwProcessId;
868             nStatus     = PSTATUS_RUNNING;
869 
870             return res;
871         }
872 
wait(wssize_t millis)873         status_t Process::wait(wssize_t millis)
874         {
875             if (nStatus != PSTATUS_RUNNING)
876                 return STATUS_BAD_STATE;
877 
878             DWORD res = ::WaitForSingleObject(hProcess, (millis < 0) ? INFINITE : millis);
879             switch (res)
880             {
881                 case WAIT_OBJECT_0:
882                 {
883                     // Obtain exit status of process
884                     DWORD code  = 0;
885                     ::GetExitCodeProcess(hProcess, &code);
886                     ::CloseHandle(hProcess);
887                     hProcess    = NULL;
888 
889                     // Update state
890                     nStatus     = PSTATUS_EXITED;
891                     nExitCode   = code;
892                     return STATUS_OK;
893                 }
894 
895                 case WAIT_TIMEOUT:
896                     return STATUS_OK;
897 
898                 default:
899                     break;
900             }
901 
902             return STATUS_UNKNOWN_ERR;
903         }
904 
close_handles()905         void Process::close_handles()
906         {
907             if (hStdIn != NULL)
908             {
909                 ::CloseHandle(hStdIn);
910                 hStdIn          = NULL;
911             }
912             if (hStdOut != NULL)
913             {
914                 ::CloseHandle(hStdOut);
915                 hStdOut         = NULL;
916             }
917             if (hStdErr != NULL)
918             {
919                 ::CloseHandle(hStdErr);
920                 hStdErr         = NULL;
921             }
922         }
923 
get_stdin()924         io::IOutStream *Process::get_stdin()
925         {
926             if ((nStatus != PSTATUS_CREATED) || (pStdIn != NULL))
927                 return pStdIn;
928 
929             HANDLE hRead = NULL, hWrite = NULL;
930             if (!::CreatePipe(&hRead, &hWrite, NULL, 0))
931                 return NULL;
932 
933             // Create stream and wrap
934             io::OutFileStream *strm = new io::OutFileStream();
935             if ((strm == NULL) || (strm->wrap_native(hWrite, true) != STATUS_OK))
936             {
937                 ::CloseHandle(hRead);
938                 ::CloseHandle(hWrite);
939                 return NULL;
940             }
941 
942             // All is OK
943             pStdIn  = strm;
944             hStdIn  = hRead;
945 
946             return pStdIn;
947         }
948 
get_stdout()949         io::IInStream *Process::get_stdout()
950         {
951             if ((nStatus != PSTATUS_CREATED) || (pStdOut != NULL))
952                 return pStdOut;
953 
954             HANDLE hRead = NULL, hWrite = NULL;
955             if (!::CreatePipe(&hRead, &hWrite, NULL, 0))
956                 return NULL;
957 
958             // Create stream and wrap
959             io::InFileStream *strm = new io::InFileStream();
960             if ((strm == NULL) || (strm->wrap_native(hRead, true) != STATUS_OK))
961             {
962                 ::CloseHandle(hRead);
963                 ::CloseHandle(hWrite);
964                 return NULL;
965             }
966 
967             // All is OK
968             pStdOut = strm;
969             hStdOut = hWrite;
970 
971             return pStdOut;
972         }
973 
get_stderr()974         io::IInStream *Process::get_stderr()
975         {
976             if ((nStatus != PSTATUS_CREATED) || (pStdErr != NULL))
977                 return pStdErr;
978 
979             HANDLE hRead = NULL, hWrite = NULL;
980             if (!::CreatePipe(&hRead, &hWrite, NULL, 0))
981                 return NULL;
982 
983             // Create stream and wrap
984             io::InFileStream *strm = new io::InFileStream();
985             if ((strm == NULL) || (strm->wrap_native(hRead, true) != STATUS_OK))
986             {
987                 ::CloseHandle(hRead);
988                 ::CloseHandle(hWrite);
989                 return NULL;
990             }
991 
992             // All is OK
993             pStdErr = strm;
994             hStdErr = hWrite;
995 
996             return pStdErr;
997         }
998 #else
drop_data(cvector<char> * v)999         static void drop_data(cvector<char> *v)
1000         {
1001             for (size_t i=0, n=v->size(); i<n; ++i)
1002             {
1003                 char *ptr = v->at(i);
1004                 if (ptr != NULL)
1005                     ::free(ptr);
1006             }
1007             v->flush();
1008         }
1009 
close_handles()1010         void Process::close_handles()
1011         {
1012             if (hStdIn >= 0)
1013             {
1014                 ::close(hStdIn);
1015                 hStdIn          = -1;
1016             }
1017             if (hStdOut >= 0)
1018             {
1019                 ::close(hStdOut);
1020                 hStdOut         = -1;
1021             }
1022             if (hStdErr >= 0)
1023             {
1024                 ::close(hStdErr);
1025                 hStdErr         = -1;
1026             }
1027         }
1028 
build_argv(cvector<char> * dst)1029         status_t Process::build_argv(cvector<char> *dst)
1030         {
1031             char *s;
1032 
1033             // Add command as argv[0]
1034             if ((s = sCommand.clone_native()) == NULL)
1035                 return STATUS_NO_MEM;
1036             if (!dst->add(s))
1037                 return STATUS_NO_MEM;
1038 
1039             // Add all other arguments
1040             for (size_t i=0, n=vArgs.size(); i<n; ++i)
1041             {
1042                 LSPString *arg = vArgs.at(i);
1043                 if (arg == NULL)
1044                     continue;
1045 
1046                 if ((s = arg->clone_native()) == NULL)
1047                     return STATUS_NO_MEM;
1048                 if (!dst->add(s))
1049                 {
1050                     ::free(s);
1051                     return STATUS_NO_MEM;
1052                 }
1053             }
1054 
1055             // Add terminator
1056             return (dst->add(static_cast<char *>(NULL))) ? STATUS_OK : STATUS_NO_MEM;
1057         }
1058 
build_envp(cvector<char> * dst)1059         status_t Process::build_envp(cvector<char> *dst)
1060         {
1061             char *s;
1062 
1063             LSPString tmp;
1064             for (size_t i=0, n=vEnv.size(); i<n; ++i)
1065             {
1066                 envvar_t *var = vEnv.at(i);
1067                 if (var == NULL)
1068                     continue;
1069                 if (!tmp.set(&var->name))
1070                     return STATUS_NO_MEM;
1071                 if (!tmp.append('='))
1072                     return STATUS_NO_MEM;
1073                 if (!tmp.append(&var->value))
1074                     return STATUS_NO_MEM;
1075 
1076                 if ((s = tmp.clone_native()) == NULL)
1077                     return STATUS_NO_MEM;
1078 
1079                 if (!dst->add(s))
1080                 {
1081                     ::free(s);
1082                     return STATUS_NO_MEM;
1083                 }
1084             }
1085             return (dst->add(static_cast<char *>(NULL))) ? STATUS_OK : STATUS_NO_MEM;
1086         }
1087 
spawn_process(const char * cmd,char * const * argv,char * const * envp)1088         status_t Process::spawn_process(const char *cmd, char * const *argv, char * const *envp)
1089         {
1090             lsp_trace("Creating child process using posix_spawn...");
1091 
1092             // Initialize spawn routines
1093             posix_spawnattr_t attr;
1094             if (::posix_spawnattr_init(&attr))
1095                 return STATUS_UNKNOWN_ERR;
1096 
1097             #if defined(__USE_GNU) || defined(POSIX_SPAWN_USEVFORK)
1098                 // Prever vfork() over fork()
1099                 if (::posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK))
1100                 {
1101                     ::posix_spawnattr_destroy(&attr);
1102                     return STATUS_UNKNOWN_ERR;
1103                 }
1104             #endif /* __USE_GNU */
1105 
1106             posix_spawn_file_actions_t actions;
1107             if (::posix_spawn_file_actions_init(&actions))
1108             {
1109                 ::posix_spawnattr_destroy(&attr);
1110                 return STATUS_UNKNOWN_ERR;
1111             }
1112 
1113             // Override STDIN, STDOUT, STDERR
1114             if (hStdIn >= 0)
1115             {
1116                 if (::posix_spawn_file_actions_adddup2(&actions, hStdIn, STDIN_FILENO))
1117                 {
1118                     ::posix_spawnattr_destroy(&attr);
1119                     return STATUS_UNKNOWN_ERR;
1120                 }
1121                 if (::posix_spawn_file_actions_addclose(&actions, hStdIn))
1122                 {
1123                     ::posix_spawnattr_destroy(&attr);
1124                     return STATUS_UNKNOWN_ERR;
1125                 }
1126             }
1127 
1128             if (hStdOut >= 0)
1129             {
1130                 if (::posix_spawn_file_actions_adddup2(&actions, hStdOut, STDOUT_FILENO))
1131                 {
1132                     ::posix_spawnattr_destroy(&attr);
1133                     return STATUS_UNKNOWN_ERR;
1134                 }
1135                 if (::posix_spawn_file_actions_addclose(&actions, hStdOut))
1136                 {
1137                     ::posix_spawnattr_destroy(&attr);
1138                     return STATUS_UNKNOWN_ERR;
1139                 }
1140             }
1141 
1142             if (hStdErr >= 0)
1143             {
1144                 if (::posix_spawn_file_actions_adddup2(&actions, hStdErr, STDERR_FILENO))
1145                 {
1146                     ::posix_spawnattr_destroy(&attr);
1147                     return STATUS_UNKNOWN_ERR;
1148                 }
1149                 if (::posix_spawn_file_actions_addclose(&actions, hStdErr))
1150                 {
1151                     ::posix_spawnattr_destroy(&attr);
1152                     return STATUS_UNKNOWN_ERR;
1153                 }
1154             }
1155 
1156             // Perform posix_spawn()
1157             pid_t pid;
1158             status_t res = STATUS_OK;
1159             while (true)
1160             {
1161                 int x = ::posix_spawnp(&pid, cmd, &actions, &attr, argv, envp);
1162                 switch (x)
1163                 {
1164                     case 0: break;
1165                     case EAGAIN: continue;
1166                     case ENOMEM: res = STATUS_NO_MEM; break;
1167                     default: res = STATUS_UNKNOWN_ERR; break;
1168                 }
1169                 break;
1170             }
1171 
1172             // Success execution?
1173             if (res == STATUS_OK)
1174             {
1175                 nPID        = pid;
1176                 nStatus     = PSTATUS_RUNNING;
1177             }
1178 
1179             ::posix_spawn_file_actions_destroy(&actions);
1180             ::posix_spawnattr_destroy(&attr);
1181 
1182             return res;
1183         }
1184 
execve_process(const char * cmd,char * const * argv,char * const * envp,bool soft_exit)1185         void Process::execve_process(const char *cmd, char * const *argv, char * const *envp, bool soft_exit)
1186         {
1187             // Override STDIN, STDOUT, STDERR
1188             if (hStdIn >= 0)
1189             {
1190                 ::dup2(hStdIn, STDIN_FILENO);
1191                 ::close(hStdIn);
1192                 hStdIn = -1;
1193             }
1194 
1195             if (hStdOut >= 0)
1196             {
1197                 ::dup2(hStdOut, STDOUT_FILENO);
1198                 ::close(hStdOut);
1199                 hStdOut = -1;
1200             }
1201 
1202             if (hStdErr >= 0)
1203             {
1204                 ::dup2(hStdErr, STDERR_FILENO);
1205                 ::close(hStdErr);
1206                 hStdErr = -1;
1207             }
1208 
1209             // Launch the process
1210             ::execve(cmd, argv, envp);
1211 
1212             lsp_trace("execve failed for pid=%d\n", int(getpid()));
1213 
1214             // Return error only if ::execvpe failed
1215             if (soft_exit)
1216                 ::_exit(STATUS_UNKNOWN_ERR);
1217             else
1218                 ::exit(STATUS_UNKNOWN_ERR);
1219         }
1220 
vfork_process(const char * cmd,char * const * argv,char * const * envp)1221         status_t Process::vfork_process(const char *cmd, char * const *argv, char * const *envp)
1222         {
1223             lsp_trace("Creating child process using vfork()...");
1224             errno           = 0;
1225             pid_t pid       = ::vfork();
1226 
1227             // Failed to fork()?
1228             if (pid < 0)
1229             {
1230                 int code        = errno;
1231                 switch (code)
1232                 {
1233                     case ENOMEM: return STATUS_NO_MEM;
1234                     case EAGAIN: return STATUS_NO_MEM;
1235                     default: return STATUS_UNKNOWN_ERR;
1236                 }
1237             }
1238 
1239             // The child process stuff
1240             if (pid == 0)
1241                 execve_process(cmd, argv, envp, true);
1242 
1243             // The parent process stuff
1244             nPID        = pid;
1245             nStatus     = PSTATUS_RUNNING;
1246 
1247             return STATUS_OK;
1248         }
1249 
fork_process(const char * cmd,char * const * argv,char * const * envp)1250         status_t Process::fork_process(const char *cmd, char * const *argv, char * const *envp)
1251         {
1252             lsp_trace("Creating child process using fork()...");
1253             errno           = 0;
1254             pid_t pid       = ::fork();
1255 
1256             // Failed to fork()?
1257             if (pid < 0)
1258             {
1259                 int code        = errno;
1260                 switch (code)
1261                 {
1262                     case ENOMEM: return STATUS_NO_MEM;
1263                     case EAGAIN: return STATUS_NO_MEM;
1264                     default: return STATUS_UNKNOWN_ERR;
1265                 }
1266             }
1267 
1268             // The child process stuff
1269             if (pid == 0)
1270                 execve_process(cmd, argv, envp, false);
1271 
1272             // The parent process stuff
1273             nPID        = pid;
1274             nStatus     = PSTATUS_RUNNING;
1275 
1276             return STATUS_OK;
1277         }
1278 
launch()1279         status_t Process::launch()
1280         {
1281             if (nStatus != PSTATUS_CREATED)
1282                 return STATUS_BAD_STATE;
1283             if (sCommand.is_empty())
1284                 return STATUS_BAD_STATE;
1285 
1286             // Copy command
1287             char *cmd = sCommand.clone_native();
1288             if (cmd == NULL)
1289                 return STATUS_NO_MEM;
1290 
1291             // Form argv
1292             cvector<char> argv;
1293             status_t res = build_argv(&argv);
1294             if (res != STATUS_OK)
1295             {
1296                 ::free(cmd);
1297                 drop_data(&argv);
1298                 return res;
1299             }
1300 
1301             // Form envp
1302             cvector<char> envp;
1303             res = build_envp(&envp);
1304             if (res == STATUS_OK)
1305             {
1306                 // Different behaviour, depending on POSIX_SPAWN_USEVFORK presence
1307                 #if defined(__USE_GNU) || defined(POSIX_SPAWN_USEVFORK)
1308                     res    = spawn_process(cmd, argv.get_array(), envp.get_array());
1309                     if (res != STATUS_OK)
1310                         res    = vfork_process(cmd, argv.get_array(), envp.get_array());
1311                 #else
1312                     res    = vfork_process(cmd, argv.get_array(), envp.get_array());
1313                     if (res != STATUS_OK)
1314                         res    = spawn_process(cmd, argv.get_array(), envp.get_array());
1315                 #endif /* __USE_GNU */
1316 
1317                 if (res != STATUS_OK)
1318                     res    = fork_process(cmd, argv.get_array(), envp.get_array());
1319             }
1320 
1321             // Close redirected file handles
1322             if (res == STATUS_OK)
1323                 close_handles();
1324 
1325             // Free temporary data and return result
1326             ::free(cmd);
1327             drop_data(&argv);
1328             drop_data(&envp);
1329             return res;
1330         }
1331 
wait(wssize_t millis)1332         status_t Process::wait(wssize_t millis)
1333         {
1334             if (nStatus != PSTATUS_RUNNING)
1335                 return STATUS_BAD_STATE;
1336 
1337             int status;
1338 
1339             if (millis < 0)
1340             {
1341                 do
1342                 {
1343                     // Wait for child process
1344                     pid_t pid = ::waitpid(nPID, &status, WUNTRACED | WCONTINUED);
1345                     if (pid < 0)
1346                     {
1347                         status = errno;
1348                         if (status == EINTR)
1349                             continue;
1350                         return STATUS_UNKNOWN_ERR;
1351                     }
1352                 } while ((!WIFEXITED(status)) && (!WIFSIGNALED(status)));
1353 
1354                 nStatus     = PSTATUS_EXITED;
1355                 nExitCode   = WEXITSTATUS(status);
1356             }
1357             else if (millis == 0)
1358             {
1359                 // Wait for child process
1360                 pid_t pid = ::waitpid(nPID, &status, WUNTRACED | WCONTINUED | WNOHANG);
1361                 if (pid < 0)
1362                 {
1363                     status = errno;
1364                     return (status == EINTR) ? STATUS_OK : STATUS_UNKNOWN_ERR;
1365                 }
1366 
1367                 // Child has exited?
1368                 if ((pid == nPID) && ((WIFEXITED(status)) || (WIFSIGNALED(status))))
1369                 {
1370                     nStatus     = PSTATUS_EXITED;
1371                     nExitCode   = WEXITSTATUS(status);
1372                 }
1373             }
1374             else
1375             {
1376                 struct timespec ts;
1377                 wssize_t deadline, left;
1378                 ::clock_gettime(CLOCK_REALTIME, &ts);
1379                 deadline    = millis + (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
1380 
1381                 while (true)
1382                 {
1383                     // Wait for child process
1384                     pid_t pid = ::waitpid(nPID, &status, WUNTRACED | WCONTINUED | WNOHANG);
1385                     if (pid < 0)
1386                     {
1387                         status = errno;
1388                         if (status == EINTR)
1389                             continue;
1390                         return STATUS_UNKNOWN_ERR;
1391                     }
1392 
1393                     // Child process has exited?
1394                     if ((pid == nPID) && ((WIFEXITED(status)) || (WIFSIGNALED(status))))
1395                         break;
1396 
1397                     // Get time
1398                     ::clock_gettime(CLOCK_REALTIME, &ts);
1399                     left    = deadline - (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
1400                     if (left <= 0)
1401                         return STATUS_OK; // Just leave, no changes
1402 
1403                     // Perform short sleep
1404                     ts.tv_sec     = 0;
1405                     ts.tv_nsec    = ((left > 50) ? 50 : left) * 1000000;
1406                     ::nanosleep(&ts, NULL);
1407                 }
1408 
1409                 nStatus     = PSTATUS_EXITED;
1410                 nExitCode   = WEXITSTATUS(status);
1411             }
1412 
1413             return STATUS_OK;
1414         }
1415 
get_stdin()1416         io::IOutStream *Process::get_stdin()
1417         {
1418             if ((nStatus != PSTATUS_CREATED) || (pStdIn != NULL))
1419                 return pStdIn;
1420 
1421             int fd[2]; // rw
1422             if (::pipe(fd) != 0)
1423                 return NULL;
1424 
1425             // Create stream and wrap
1426             io::OutFileStream *strm = new io::OutFileStream();
1427             if ((strm == NULL) || (strm->wrap_native(fd[1], true) != STATUS_OK))
1428             {
1429                 ::close(fd[0]);
1430                 ::close(fd[1]);
1431                 return NULL;
1432             }
1433 
1434             // All is OK
1435             pStdIn  = strm;
1436             hStdIn  = fd[0];
1437 
1438             return pStdIn;
1439         }
1440 
get_stdout()1441         io::IInStream *Process::get_stdout()
1442         {
1443             if ((nStatus != PSTATUS_CREATED) || (pStdOut != NULL))
1444                 return pStdOut;
1445 
1446             int fd[2]; // rw
1447             if (::pipe(fd) != 0)
1448                 return NULL;
1449 
1450             // Create stream and wrap
1451             io::InFileStream *strm = new io::InFileStream();
1452             if ((strm == NULL) || (strm->wrap_native(fd[0], true) != STATUS_OK))
1453             {
1454                 ::close(fd[0]);
1455                 ::close(fd[1]);
1456                 return NULL;
1457             }
1458 
1459             // All is OK
1460             pStdOut = strm;
1461             hStdOut = fd[1];
1462 
1463             return pStdOut;
1464         }
1465 
get_stderr()1466         io::IInStream *Process::get_stderr()
1467         {
1468             if ((nStatus != PSTATUS_CREATED) || (pStdErr != NULL))
1469                 return pStdErr;
1470 
1471             int fd[2]; // rw
1472             if (::pipe(fd) != 0)
1473                 return NULL;
1474 
1475             // Create stream and wrap
1476             io::InFileStream *strm = new io::InFileStream();
1477             if ((strm == NULL) || (strm->wrap_native(fd[0], true) != STATUS_OK))
1478             {
1479                 ::close(fd[0]);
1480                 ::close(fd[1]);
1481                 return NULL;
1482             }
1483 
1484             // All is OK
1485             pStdErr = strm;
1486             hStdErr = fd[1];
1487 
1488             return pStdErr;
1489         }
1490 
1491 
1492 #endif /* PLATFORM_WINDOWS */
1493 
copy_env()1494         status_t Process::copy_env()
1495         {
1496             cvector<envvar_t> env;
1497             LSPString k, v;
1498 
1499         #ifdef PLATFORM_WINDOWS
1500             for (WCHAR *item = GetEnvironmentStringsW(); (*item) != 0; )
1501             {
1502                 size_t len  = wcslen(item);
1503 
1504                 // Fetch environment variable
1505                 if (!k.set_utf16(item, len))
1506                 {
1507                     destroy_env(&env);
1508                     return STATUS_NO_MEM;
1509                 }
1510                 item    += (len + 1); // length + terminating character
1511         #else
1512             for (char **item=environ; *item != NULL; ++item)
1513             {
1514                 // Fetch environment variable
1515                 if (!k.set_native(*item))
1516                 {
1517                     destroy_env(&env);
1518                     return STATUS_NO_MEM;
1519                 }
1520         #endif /* PLATFORM_WINDOWS */
1521 
1522                 // Parse record
1523                 ssize_t idx = k.index_of('=');
1524                 if (idx >= 0)
1525                 {
1526                     if (!v.set(&k, idx+1))
1527                     {
1528                         destroy_env(&env);
1529                         return STATUS_NO_MEM;
1530                     }
1531                     if (!k.truncate(idx))
1532                     {
1533                         destroy_env(&env);
1534                         return STATUS_NO_MEM;
1535                     }
1536                 }
1537 
1538                 // Allocate && add env var
1539                 envvar_t *var = new envvar_t();
1540                 if ((var == NULL) || (!env.add(var)))
1541                 {
1542                     destroy_env(&env);
1543                     return STATUS_NO_MEM;
1544                 }
1545 
1546                 // Store value to env
1547                 var->name.swap(&k);
1548                 var->value.swap(&v);
1549 
1550             } // for
1551 
1552             // Commit result
1553             vEnv.swap_data(&env);
1554             destroy_env(&env);
1555 
1556             return STATUS_OK;
1557         }
1558     } /* namespace ipc */
1559 } /* namespace lsp */
1560