19 #include <ucommon-config.h> 28 #ifndef UCOMMON_SYSRUNTIME 45 #include <sys/ioctl.h> 57 #ifdef HAVE_SYS_RESOURCE_H 59 #include <sys/resource.h> 65 #define setlocale(s, t) 68 #ifndef HAVE_LIBINTL_H 75 #define dgettext(d, s) s 77 #define bindtextdomain(s, t) 90 #define WEXITSTATUS(status) ((unsigned)(status) >> 8) 94 #define _PATH_TTY "/dev/tty" 121 while(longopt && *longopt ==
'-')
154 shell::
Option(short_option, long_option, NULL, help_string)
170 shell::
Option(short_option, long_option, type, help_string)
184 number = strtol(value, &endptr, 0);
185 if(!endptr || *endptr != 0)
192 shell::
Option(short_option, long_option, type, help_string)
214 number = strtol(value, &endptr, 0);
215 if(!endptr || *endptr != 0)
232 shell::
Option(short_option, long_option, type, help_string)
248 shell::charopt::charopt(
char short_option,
const char *long_option,
const char *help_string,
const char *type,
char def_value) :
249 shell::
Option(short_option, long_option, type, help_string)
269 number = strtol(value, &endptr, 0);
270 if(!endptr || *endptr != 0)
273 if(number < 0 || number > 255)
276 code = (char)(number);
326 char *ext = strrchr(
_argv0,
'.');
368 _TEXT(
"missing command line arguments"),
369 _TEXT(
"missing argument for option"),
370 _TEXT(
"option does not have argument"),
371 _TEXT(
"unknown command option"),
372 _TEXT(
"option already used"),
373 _TEXT(
"invalid argument used"),
374 _TEXT(
"numeric value already set"),
411 size_t hp = 0,
count = 0;
466 if(*hs ==
'\n' || (((*hs ==
' ' || *hs ==
'\t')) && (hp > 75))) {
470 else if(*hs ==
'\t') {
491 assert(
string != NULL);
502 if(isspace(*cp) && active && !
quote) {
508 if(*cp ==
'\'' && !active) {
512 if(*cp ==
'\"' && !active) {
516 if(*cp ==
quote && active) {
524 if(!isspace(*cp) && !active) {
545 va_start(
args, format);
581 if(
eq(
argv[argp],
"--")) {
585 arg = opt =
argv[argp];
590 if(opt[0] ==
'-' && opt[1] >=
'0' && opt[1] <=
'9') {
605 if(opt[0] ==
'+' && opt[1] >=
'0' && opt[1] <=
'9') {
647 if(opt[len] ==
'=') {
648 value = opt + len + 1;
653 value =
argv[argp++];
671 if(
eq(arg,
"--", 2)) {
672 char *cp = strchr(arg,
'=');
680 while(*(++arg) != 0) {
698 value =
argv[argp++];
719 #if defined(_MSWINDOWS_) && defined(_MSC_VER) 722 WIN32_FIND_DATA entry;
729 fn = strrchr(*
_argv,
'/');
732 fn = strrchr(*
_argv,
'\\');
733 if(!fn && arg[1] ==
':')
734 fn = strrchr(*
_argv,
':');
744 if(*fn !=
'*' && fn[strlen(fn) - 1] !=
'*' && !strchr(fn,
'?'))
749 if(len >=
sizeof(dirname))
750 len =
sizeof(dirname) - 1;
755 len = strlen(dirname);
757 String::set(dirname + len,
sizeof(dirname) - len, fn);
760 dir = FindFirstFile(dirname, &entry);
765 String::set(dirname + len,
sizeof(dirname) - len, fn);
770 argitem->
enlist(&arglist);
772 }
while(FindNextFile(
dir, &entry));
779 argitem->
enlist(&arglist);
793 size_t len = strlen(buf);
795 if(buf[len - 1] !=
'\n') {
804 va_start(
args, format);
805 if(!
eq(
"*** ", format, 4))
806 fputs(
"*** ", stderr);
807 vfprintf(stderr, format,
args);
814 if(
eq(
"*** ", format, 4)) {
816 const char *cp = format;
817 while(isalnum(*cp) || *cp ==
'-' || *cp ==
'.')
819 if(*cp ==
':' && cp[1] ==
' ')
822 vsyslog(LOG_ERR, format,
args);
837 size_t len = strlen(buf);
839 if(buf[len - 1] !=
'\n') {
848 va_start(
args, format);
849 if(!
eq(
"*** ", format, 4))
850 fputs(
"*** ", stderr);
851 vfprintf(stderr, format,
args);
858 if(
eq(
"*** ", format, 4)) {
860 const char *cp = format;
861 while(isalnum(*cp) || *cp ==
'-' || *cp ==
'.')
863 if(*cp ==
':' && cp[1] ==
' ')
866 vsyslog(LOG_CRIT, format,
args);
877 va_start(
args, format);
888 static void pathfinder(
const char *name,
char *buf,
size_t size)
897 if(!GetEnvironmentVariable(
"PATH", path,
sizeof(path)))
900 if(strchr(name,
'/') || strchr(name,
'\\') || strchr(name,
':'))
903 while(NULL != (element =
String::token(path, &tokbuf,
";"))) {
904 snprintf(buf,
sizeof(buf),
"%s\\%s", element, name);
905 ext = strrchr(buf,
'.');
915 ext = strrchr(buf,
'.');
927 DWORD
size =
sizeof(buf);
930 GetUserName(buf, &
size);
945 result =
str(
"~\\SOFTWARE\\Services\\Control");
954 if(GetEnvironmentVariable(
"SystemRoot", buf,
sizeof(buf)))
958 if(GetEnvironmentVariable(
"USERPROFILE", buf,
sizeof(buf)))
963 if(GetEnvironmentVariable(
"APPDATA", buf,
sizeof(buf))) {
969 if(GetEnvironmentVariable(
"USERPROFILE", buf,
sizeof(buf))) {
976 if(GetEnvironmentVariable(
"TEMP", buf,
sizeof(buf))) {
981 if(GetEnvironmentVariable(
"APPDATA", buf,
sizeof(buf))) {
991 snprintf(buf,
sizeof(buf),
"$$%ld$$.tmp", (
long)GetCurrentProcessId());
995 if(GetEnvironmentVariable(
"SystemRoot", buf,
sizeof(buf)))
1005 result ^= UCOMMON_PREFIX;
1008 result ^= UCOMMON_PREFIX;
1018 const char *
shell::getenv(
const char *
id,
const char *value)
1022 const char *keyid = NULL;
1024 if(GetEnvironmentVariable(
id, buf,
sizeof(buf)))
1035 snprintf(
path,
sizeof(
path),
"Default Environment\\%s", keyid);
1037 if(RegOpenKey(HKEY_CLASSES_ROOT,
path, &key) == ERROR_SUCCESS) {
1038 LONG dlen =
sizeof(buf);
1040 RegQueryValueA(key,
id, (LPTSTR)buf, &dlen);
1053 PROCESS_INFORMATION pi;
1058 ep =
new char[4096];
1060 while(envp && *envp && len < 4090) {
1062 len += strlen(*(envp++)) + 1;
1068 if(!GetEnvironmentVariable(
"SHELL", cmdspec,
sizeof(cmdspec)))
1069 GetEnvironmentVariable(
"ComSpec", cmdspec,
sizeof(cmdspec));
1071 if(!CreateProcess((CHAR *)cmdspec, (CHAR *)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, ep, NULL, NULL, &pi)) {
1079 CloseHandle(pi.hThread);
1080 int status =
wait(pi.hProcess);
1088 if(WaitForSingleObject(pid, INFINITE) == WAIT_FAILED) {
1092 GetExitCodeProcess(pid, &code);
1100 PROCESS_INFORMATION pi;
1111 memset(&si, 0,
sizeof(STARTUPINFO));
1112 si.cb =
sizeof(STARTUPINFO);
1115 ep =
new char[4096];
1117 while(envp && *envp && len < 4090) {
1119 len += strlen(*(envp++)) + 1;
1125 pathfinder(
path, filename,
sizeof(filename));
1126 char *args =
new char[32768];
1137 for(pos = 0; pos < 3; ++pos) {
1142 stdfd = GetStdHandle(STD_INPUT_HANDLE);
1146 stdfd = GetStdHandle(STD_OUTPUT_HANDLE);
1150 stdfd = GetStdHandle(STD_ERROR_HANDLE);
1154 DuplicateHandle(GetCurrentProcess(), stdfd,
1155 GetCurrentProcess(), &dups[pos], 0,
1156 TRUE, DUPLICATE_SAME_ACCESS);
1163 si.hStdInput = stdfd;
1166 si.hStdOutput = stdfd;
1169 si.hStdError = stdfd;
1173 si.dwFlags = STARTF_USESTDHANDLES;
1176 if(!CreateProcess((CHAR *)filename, (CHAR *)args, NULL, NULL, TRUE, 0, ep, NULL, &si, &pi))
1180 CloseHandle(pi.hThread);
1186 for(pos = 0; pos < 3; ++pos) {
1188 CloseHandle(dups[pos]);
1201 static BOOL WINAPI _stop(DWORD code)
1213 SetConsoleCtrlHandler((PHANDLER_ROUTINE)_stop, TRUE);
1226 const char *name =
_argv0;
1236 name = ::strdup(name);
1238 SERVICE_TABLE_ENTRY servicetable[] = {
1239 {(LPSTR)name, (LPSERVICE_MAIN_FUNCTION)entry},
1243 if(!StartServiceCtrlDispatcher(servicetable))
1244 errexit(1,
"*** %s: %s\n", name,
_TEXT(
"failed service start"));
1254 PROCESS_INFORMATION pi;
1266 memset(&si, 0,
sizeof(STARTUPINFO));
1267 si.cb =
sizeof(STARTUPINFO);
1270 ep =
new char[4096];
1272 while(envp && *envp && len < 4090) {
1274 len += strlen(*(envp++)) + 1;
1280 pathfinder(
path, filename,
sizeof(filename));
1281 char *args =
new char[32768];
1292 for(pos = 0; pos < 3; ++pos) {
1297 stdfd = GetStdHandle(STD_INPUT_HANDLE);
1301 stdfd = GetStdHandle(STD_OUTPUT_HANDLE);
1305 stdfd = GetStdHandle(STD_ERROR_HANDLE);
1309 DuplicateHandle(GetCurrentProcess(), stdfd,
1310 GetCurrentProcess(), &dups[pos], 0,
1311 TRUE, DUPLICATE_SAME_ACCESS);
1318 si.hStdInput = stdfd;
1321 si.hStdOutput = stdfd;
1324 si.hStdError = stdfd;
1328 si.dwFlags = STARTF_USESTDHANDLES;
1331 if(!CreateProcess((CHAR *)filename, (CHAR *)args, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, ep, NULL, &si, &pi)) {
1337 CloseHandle(pi.hThread);
1344 for(pos = 0; pos < 3; ++pos) {
1346 CloseHandle(dups[pos]);
1357 fputs(prompt, stderr);
1359 while(pos <
size - 1) {
1360 buffer[pos] = (char)getch();
1363 else if(
buffer[pos] ==
'\b' && pos)
1375 fputs(prompt, stdout);
1379 return (
char)getch();
1384 if(!TerminateProcess(pid, 255))
1396 fputs(prompt, stdout);
1398 while(pos <
size - 1) {
1399 buffer[pos] = (char)getch();
1402 else if(
buffer[pos] ==
'\b' && pos) {
1403 fputs(
"\b \b", stdout);
1407 fputc(
buffer[pos], stdout);
1421 #if defined(HAVE_TERMIOS_H) 1422 static struct termios io_prior, io_current;
1423 #elif defined(HAVE_TERMIO_H) 1424 static struct termio io_prior, io_current;
1429 #if defined(HAVE_TERMIOS_H) 1430 tcgetattr(fd, &io_prior);
1431 tcgetattr(fd, &io_current);
1432 io_current.c_lflag &= ~ECHO;
1433 tcsetattr(fd, TCSAFLUSH, &io_current);
1434 #elif defined(HAVE_TERMIO_H) 1435 ioctl(fd, TCGETA, &io_prior);
1436 ioctl(fd, TCGETA, &io_current);
1437 io_current.c_lflag &= ~ECHO;
1438 ioctl(fd, TCSETA, &io_current);
1444 #if defined(HAVE_TERMIOS_H) 1445 tcsetattr(fd, TCSAFLUSH, &io_prior);
1446 #elif defined(HAVE_TERMIO_H) 1447 ioctl(fd, TCSETA, &io_prior);
1459 fputs(prompt, stdout);
1461 while(pos <
size - 1) {
1462 buffer[pos] = getc(stdin);
1465 else if(
buffer[pos] ==
'\b' && pos) {
1466 fputs(
"\b \b", stdout);
1470 fputc(
buffer[pos], stdout);
1486 int fd = ::open(
"/dev/tty", O_RDONLY);
1491 fputs(prompt, stderr);
1497 #if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H) 1498 fputs(
"\n", stderr);
1513 fputs(prompt, stdout);
1514 int ch = getc(stdin);
1522 #ifdef HAVE_REALPATH 1523 char *path0 = realpath(
argv0, NULL);
1528 char *cp = strrchr(path0,
'/');
1532 cp = strrchr(path0,
'/');
1533 if(cp && (
eq(cp,
"/bin") ||
eq(cp,
"/sbin"))) {
1543 const char *
id =
::getenv(
"LOGNAME");
1554 const char *home = NULL;
1641 snprintf(buf,
sizeof(buf),
".$$%ld$$.tmp", (
long)getpid());
1676 assert(cmd != NULL);
1682 int max =
sizeof(fd_set) * 8;
1684 #ifdef RLIMIT_NOFILE 1687 if(!getrlimit(RLIMIT_NOFILE, &rlim))
1688 max = rlim.rlim_max;
1696 if(::waitpid(pid, &status, 0) != pid)
1701 for(
int fd = 3; fd <
max; ++fd)
1704 while(envp && *envp) {
1706 ep = strchr(symname,
'=');
1709 cp = strchr(*envp,
'=');
1712 ::setenv(symname, cp, 1);
1717 ::signal(SIGHUP, SIG_DFL);
1718 ::signal(SIGABRT, SIG_DFL);
1719 ::signal(SIGQUIT, SIG_DFL);
1720 ::signal(SIGINT, SIG_DFL);
1721 ::signal(SIGCHLD, SIG_DFL);
1722 ::signal(SIGPIPE, SIG_DFL);
1723 ::signal(SIGUSR1, SIG_DFL);
1724 ::execlp(
"/bin/sh",
"sh",
"-c", cmd, NULL);
1736 waitpid(pid, &status, 0);
1737 if(WIFSIGNALED(status))
1787 const char *dev =
"/dev/null";
1798 signal(SIGTTOU, SIG_IGN);
1802 signal(SIGTTIN, SIG_IGN);
1806 signal(SIGTSTP, SIG_IGN);
1811 crit(pid == 0,
"detach without process");
1813 #if defined(SIGTSTP) && defined(TIOCNOTTY) 1814 crit(setpgid(0, getpid()) == 0,
"detach without process group");
1815 if((fd = open(
_PATH_TTY, O_RDWR)) >= 0) {
1816 ioctl(fd, TIOCNOTTY, NULL);
1822 crit(setpgrp() == 0,
"detach without process group");
1824 crit(setpgid(0, getpid()) == 0,
"detach without process group");
1826 signal(SIGHUP, SIG_IGN);
1830 crit(pid == 0,
"detach without process");
1833 fd = open(dev, O_RDWR);
1848 const char *dev =
"/dev/null";
1856 signal(SIGTTOU, SIG_IGN);
1860 signal(SIGTTIN, SIG_IGN);
1864 signal(SIGTSTP, SIG_IGN);
1869 crit(pid == 0,
"detach without process");
1871 #if defined(SIGTSTP) && defined(TIOCNOTTY) 1872 crit(setpgid(0, getpid()) == 0,
"detach without process group");
1873 if((fd = open(
_PATH_TTY, O_RDWR)) >= 0) {
1874 ioctl(fd, TIOCNOTTY, NULL);
1880 crit(setpgrp() == 0,
"detach without process group");
1882 crit(setpgid(0, getpid()) == 0,
"detach without process group");
1884 signal(SIGHUP, SIG_IGN);
1888 crit(pid == 0,
"detach without process");
1891 fd = open(dev, O_RDWR);
1910 int max =
sizeof(fd_set) * 8;
1911 #ifdef RLIMIT_NOFILE 1914 if(!getrlimit(RLIMIT_NOFILE, &rlim))
1915 max = rlim.rlim_max;
1925 ::signal(SIGQUIT, SIG_DFL);
1926 ::signal(SIGINT, SIG_DFL);
1927 ::signal(SIGCHLD, SIG_DFL);
1928 ::signal(SIGPIPE, SIG_DFL);
1929 ::signal(SIGHUP, SIG_DFL);
1930 ::signal(SIGABRT, SIG_DFL);
1931 ::signal(SIGUSR1, SIG_DFL);
1934 ::signal(SIGTTOU, SIG_IGN);
1938 ::signal(SIGTTIN, SIG_IGN);
1942 ::signal(SIGTSTP, SIG_IGN);
1945 for(fd = 0; fd < 3; ++fd) {
1947 ::dup2(stdio[fd], fd);
1952 for(fd = 3; fd <
max; ++fd)
1955 #if defined(SIGTSTP) && defined(TIOCNOTTY) 1956 if(setpgid(0, getpid()) == -1)
1959 if((fd = open(
"/dev/tty", O_RDWR)) >= 0) {
1960 ::ioctl(fd, TIOCNOTTY, NULL);
1969 if(setpgid(0, getpid()) == -1)
1973 if(getppid() != 1) {
1974 if((pid = fork()) < 0)
1981 for(fd = 0; fd < 3; ++fd) {
1984 fd_t tmp = ::open(
"/dev/null", O_RDWR);
1991 while(envp && *envp) {
1993 ep = strchr(symname,
'=');
1996 cp = strchr(*envp,
'=');
1999 ::setenv(symname, cp, 1);
2004 if(strchr(
path,
'/'))
2019 int max =
sizeof(fd_set) * 8;
2020 #ifdef RLIMIT_NOFILE 2023 if(!getrlimit(RLIMIT_NOFILE, &rlim))
2024 max = rlim.rlim_max;
2034 ::signal(SIGQUIT, SIG_DFL);
2035 ::signal(SIGINT, SIG_DFL);
2036 ::signal(SIGCHLD, SIG_DFL);
2037 ::signal(SIGPIPE, SIG_DFL);
2038 ::signal(SIGHUP, SIG_DFL);
2039 ::signal(SIGABRT, SIG_DFL);
2040 ::signal(SIGUSR1, SIG_DFL);
2042 for(fd = 0; fd < 3; ++fd) {
2044 ::dup2(stdio[fd], fd);
2047 for(fd = 3; fd <
max; ++fd)
2050 while(envp && *envp) {
2052 ep = strchr(symname,
'=');
2055 cp = strchr(*envp,
'=');
2058 ::setenv(symname, cp, 1);
2063 if(strchr(
path,
'/'))
2085 if(kill(pid, SIGTERM))
2092 const char *
shell::texts(
const char *singular,
const char *plural,
unsigned long value)
2095 return ::ngettext(singular, plural, value);
2145 #if _POSIX_PRIORITY_SCHEDULING > 0 2146 int policy = SCHED_OTHER;
2151 struct sched_param sparam;
2152 int min = sched_get_priority_min(policy);
2153 int max = sched_get_priority_max(policy);
2154 int pri = (int)level;
2163 setpriority(PRIO_PROCESS, 0, -level);
2164 memset(&sparam, 0,
sizeof(sparam));
2165 sparam.sched_priority = pri;
2166 sched_setscheduler(0, policy, &sparam);
2175 assert(fmt != NULL && *fmt != 0);
2180 level += (unsigned)
DEBUG0;
2185 va_start(
args, fmt);
2186 vsnprintf(buf,
sizeof(buf), fmt,
args);
2189 if(fmt[strlen(fmt) - 1] ==
'\n')
2190 fprintf(stderr,
"%s: %s",
errname, buf);
2192 fprintf(stderr,
"%s: %s\n",
errname, buf);
2195 #ifdef HAVE_SYSLOG_H 2197 #ifndef LOG_AUTHPRIV 2198 #define LOG_AUTHPRIV LOG_AUTH 2201 void shell::log(
const char *name, loglevel_t level, logmode_t mode, logproc_t handler)
2215 ::openlog(name, LOG_CONS, LOG_DAEMON);
2218 ::openlog(name, 0, LOG_USER);
2221 ::openlog(name, LOG_CONS, LOG_DAEMON);
2224 ::openlog(name, LOG_CONS, LOG_AUTHPRIV);
2231 assert(fmt != NULL && *fmt != 0);
2240 va_start(args, fmt);
2241 vsnprintf(buf,
sizeof(buf), fmt, args);
2252 level = LOG_WARNING;
2264 ::syslog(level | LOG_AUTHPRIV,
"%s", buf);
2266 if(level == LOG_CRIT)
2270 void shell::log(loglevel_t loglevel,
const char *fmt, ...)
2272 assert(fmt != NULL && *fmt != 0);
2281 va_start(args, fmt);
2282 vsnprintf(buf,
sizeof(buf), fmt, args);
2292 if(fmt[strlen(fmt) - 1] ==
'\n')
2293 fprintf(stderr,
"%s: %s",
errname, buf);
2295 fprintf(stderr,
"%s: %s\n",
errname, buf);
2308 level = LOG_WARNING;
2321 if(fmt[strlen(fmt) - 1] ==
'\n')
2322 fprintf(stderr,
"%s: %s",
errname, buf);
2324 fprintf(stderr,
"%s: %s\n",
errname, buf);
2326 ::syslog(level,
"%s", buf);
2328 if(level == LOG_CRIT)
2346 assert(fmt != NULL && *fmt != 0);
2354 va_start(
args, fmt);
2355 vsnprintf(buf,
sizeof(buf), fmt,
args);
2358 if(fmt[strlen(fmt) - 1] ==
'\n')
2359 fprintf(stderr,
"%s: %s",
errname, buf);
2361 fprintf(stderr,
"%s: %s\n",
errname, buf);
2363 if(loglevel ==
FAIL)
2369 assert(fmt != NULL && *fmt != 0);
2377 va_start(
args, fmt);
2378 vsnprintf(buf,
sizeof(buf), fmt,
args);
2387 if(fmt[strlen(fmt) - 1] ==
'\n')
2388 fprintf(stderr,
"%s: %s",
errname, buf);
2390 fprintf(stderr,
"%s: %s\n",
errname, buf);
2394 if(fmt[strlen(fmt) - 1] ==
'\n')
2395 fprintf(stderr,
"%s: %s",
errname, buf);
2397 fprintf(stderr,
"%s: %s\n",
errname, buf);
2399 if(loglevel ==
FAIL)
2410 unsigned head =
count(list);
2414 memcpy(newargs, list, head *
sizeof(
char **));
2415 newargs[head++] =
argv0;
2417 memcpy(&newargs[head],
argv,
args *
sizeof(
char **));
2419 newargs[
argc] = NULL;
2420 execvp(*list, newargs);
2491 if(*
dir ==
'\\' || *
dir ==
'/')
2504 if(*
dir ==
'\\' || *
dir ==
'/')
2508 if(strchr(*
result,
'\\'))