summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Salyzyn2015-01-26 12:46:44 -0600
committerMark Salyzyn2015-02-25 11:44:18 -0600
commit2d3f38a6b8e724749b59d201a01b35fa0951141e (patch)
tree99855d3dd759307ced68e36ad7206747e55bf51d
parent72bf2a710089b5fff42059d5946fe7bdd6b12480 (diff)
downloadplatform-system-core-2d3f38a6b8e724749b59d201a01b35fa0951141e.tar.gz
platform-system-core-2d3f38a6b8e724749b59d201a01b35fa0951141e.tar.xz
platform-system-core-2d3f38a6b8e724749b59d201a01b35fa0951141e.zip
liblog: introduce ANDROID_LOG_* flags
Move away from using POSIX open(2) flags and introduce ANDROID_LOG_* flags to replace them. Add security by preventing random mode flags from getting into underlying POSIX calls. ANDROID_LOG_* flags overlap POSIX O_* flag definitions. Change-Id: Ib32bb64c287e8bf150be62242e1ba46bb37839fc
-rw-r--r--debuggerd/tombstone.cpp2
-rw-r--r--include/log/logger.h6
-rw-r--r--liblog/README13
-rw-r--r--liblog/log_read.c12
-rw-r--r--liblog/log_read_kern.c22
-rw-r--r--liblog/tests/libc_test.cpp4
-rw-r--r--liblog/tests/liblog_benchmark.cpp4
-rw-r--r--liblog/tests/liblog_test.cpp14
-rw-r--r--logcat/logcat.cpp8
9 files changed, 46 insertions, 39 deletions
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index 4233d4674..e927ea3af 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -453,7 +453,7 @@ static void dump_log_file(
453 } 453 }
454 454
455 logger_list = android_logger_list_open( 455 logger_list = android_logger_list_open(
456 android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid); 456 android_name_to_log_id(filename), ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tail, pid);
457 457
458 if (!logger_list) { 458 if (!logger_list) {
459 ALOGE("Unable to open %s: %s\n", filename, strerror(errno)); 459 ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
diff --git a/include/log/logger.h b/include/log/logger.h
index 53be1d308..570f02bc7 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -154,6 +154,12 @@ ssize_t android_logger_get_prune_list(struct logger_list *logger_list,
154int android_logger_set_prune_list(struct logger_list *logger_list, 154int android_logger_set_prune_list(struct logger_list *logger_list,
155 char *buf, size_t len); 155 char *buf, size_t len);
156 156
157#define ANDROID_LOG_RDONLY O_RDONLY
158#define ANDROID_LOG_WRONLY O_WRONLY
159#define ANDROID_LOG_RDWR O_RDWR
160#define ANDROID_LOG_ACCMODE O_ACCMODE
161#define ANDROID_LOG_NONBLOCK O_NONBLOCK
162
157struct logger_list *android_logger_list_alloc(int mode, 163struct logger_list *android_logger_list_alloc(int mode,
158 unsigned int tail, 164 unsigned int tail,
159 pid_t pid); 165 pid_t pid);
diff --git a/liblog/README b/liblog/README
index d7472e4a1..0676aecb8 100644
--- a/liblog/README
+++ b/liblog/README
@@ -111,20 +111,21 @@ DESCRIPTION
111 ger_list_alloc, calling in turn the android_logger_open for each log 111 ger_list_alloc, calling in turn the android_logger_open for each log
112 id. Each entry can be retrieved with android_logger_list_read. The 112 id. Each entry can be retrieved with android_logger_list_read. The
113 log(s) can be closed with android_logger_list_free. The logs should be 113 log(s) can be closed with android_logger_list_free. The logs should be
114 opened with an O_RDONLY mode. O_NDELAY mode will report when the log 114 opened with an ANDROID_LOG_RDONLY mode. ANDROID_LOG_NONBLOCK mode
115 reading is done with an EAGAIN error return code, otherwise the 115 will report when the log reading is done with an EAGAIN error return
116 android_logger_list_read call will block for new entries. 116 code, otherwise the android_logger_list_read call will block for new
117 entries.
117 118
118 The value returned by android_logger_open can be used as a parameter to 119 The value returned by android_logger_open can be used as a parameter to
119 the android_logger_clear function to empty the sub-log. It is recom‐ 120 the android_logger_clear function to empty the sub-log. It is recom‐
120 mended to only open log O_WRONLY. 121 mended to only open log ANDROID_LOG_WRONLY in that case.
121 122
122 The value returned by android_logger_open can be used as a parameter to 123 The value returned by android_logger_open can be used as a parameter to
123 the android_logger_get_log_(size|readable_size|version) to retrieve the 124 the android_logger_get_log_(size|readable_size|version) to retrieve the
124 sub-log maximum size, readable size and log buffer format protocol ver‐ 125 sub-log maximum size, readable size and log buffer format protocol ver‐
125 sion respectively. android_logger_get_id returns the id that was used 126 sion respectively. android_logger_get_id returns the id that was used
126 when opening the sub-log. It is recommended to open the log O_RDONLY 127 when opening the sub-log. It is recommended to open the log
127 in these cases. 128 ANDROID_LOG_RDONLY in these cases.
128 129
129SEE ALSO 130SEE ALSO
130 syslogd(8) 131 syslogd(8)
diff --git a/liblog/log_read.c b/liblog/log_read.c
index dbed886da..0b126cf4d 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -582,7 +582,7 @@ int android_logger_list_read(struct logger_list *logger_list,
582 return -EINVAL; 582 return -EINVAL;
583 } 583 }
584 584
585 if (logger_list->mode & O_NONBLOCK) { 585 if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
586 memset(&ignore, 0, sizeof(ignore)); 586 memset(&ignore, 0, sizeof(ignore));
587 ignore.sa_handler = caught_signal; 587 ignore.sa_handler = caught_signal;
588 sigemptyset(&ignore.sa_mask); 588 sigemptyset(&ignore.sa_mask);
@@ -602,7 +602,7 @@ int android_logger_list_read(struct logger_list *logger_list,
602 } 602 }
603 603
604 strcpy(buffer, 604 strcpy(buffer,
605 (logger_list->mode & O_NONBLOCK) ? "dumpAndClose" : "stream"); 605 (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
606 cp = buffer + strlen(buffer); 606 cp = buffer + strlen(buffer);
607 607
608 strcpy(cp, " lids"); 608 strcpy(cp, " lids");
@@ -640,14 +640,14 @@ int android_logger_list_read(struct logger_list *logger_list,
640 cp += ret; 640 cp += ret;
641 } 641 }
642 642
643 if (logger_list->mode & O_NONBLOCK) { 643 if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
644 /* Deal with an unresponsive logd */ 644 /* Deal with an unresponsive logd */
645 sigaction(SIGALRM, &ignore, &old_sigaction); 645 sigaction(SIGALRM, &ignore, &old_sigaction);
646 old_alarm = alarm(30); 646 old_alarm = alarm(30);
647 } 647 }
648 ret = write(sock, buffer, cp - buffer); 648 ret = write(sock, buffer, cp - buffer);
649 e = errno; 649 e = errno;
650 if (logger_list->mode & O_NONBLOCK) { 650 if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
651 if (e == EINTR) { 651 if (e == EINTR) {
652 e = ETIMEDOUT; 652 e = ETIMEDOUT;
653 } 653 }
@@ -673,7 +673,7 @@ int android_logger_list_read(struct logger_list *logger_list,
673 while(1) { 673 while(1) {
674 memset(log_msg, 0, sizeof(*log_msg)); 674 memset(log_msg, 0, sizeof(*log_msg));
675 675
676 if (logger_list->mode & O_NONBLOCK) { 676 if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
677 /* particularily useful if tombstone is reporting for logd */ 677 /* particularily useful if tombstone is reporting for logd */
678 sigaction(SIGALRM, &ignore, &old_sigaction); 678 sigaction(SIGALRM, &ignore, &old_sigaction);
679 old_alarm = alarm(30); 679 old_alarm = alarm(30);
@@ -681,7 +681,7 @@ int android_logger_list_read(struct logger_list *logger_list,
681 /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */ 681 /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
682 ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0); 682 ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
683 e = errno; 683 e = errno;
684 if (logger_list->mode & O_NONBLOCK) { 684 if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
685 if ((ret == 0) || (e == EINTR)) { 685 if ((ret == 0) || (e == EINTR)) {
686 e = EAGAIN; 686 e = EAGAIN;
687 ret = -1; 687 ret = -1;
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
index 41b8a51a4..bdc7b1860 100644
--- a/liblog/log_read_kern.c
+++ b/liblog/log_read_kern.c
@@ -75,10 +75,10 @@ const char *android_log_id_to_name(log_id_t log_id)
75 75
76static int accessmode(int mode) 76static int accessmode(int mode)
77{ 77{
78 if ((mode & O_ACCMODE) == O_WRONLY) { 78 if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_WRONLY) {
79 return W_OK; 79 return W_OK;
80 } 80 }
81 if ((mode & O_ACCMODE) == O_RDWR) { 81 if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR) {
82 return R_OK | W_OK; 82 return R_OK | W_OK;
83 } 83 }
84 return R_OK; 84 return R_OK;
@@ -117,7 +117,7 @@ log_id_t android_name_to_log_id(const char *logName)
117 ++b; 117 ++b;
118 } 118 }
119 119
120 ret = check_allocate_accessible(&n, b, O_RDONLY); 120 ret = check_allocate_accessible(&n, b, ANDROID_LOG_RDONLY);
121 free(n); 121 free(n);
122 if (ret) { 122 if (ret) {
123 return ret; 123 return ret;
@@ -201,8 +201,8 @@ static int logger_ioctl(struct logger *logger, int cmd, int mode)
201 return -EFAULT; 201 return -EFAULT;
202 } 202 }
203 203
204 if (((mode & O_ACCMODE) == O_RDWR) 204 if (((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR)
205 || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { 205 || (((mode ^ logger->top->mode) & ANDROID_LOG_ACCMODE) == 0)) {
206 return ioctl(logger->fd, cmd); 206 return ioctl(logger->fd, cmd);
207 } 207 }
208 208
@@ -227,13 +227,13 @@ static int logger_ioctl(struct logger *logger, int cmd, int mode)
227 227
228int android_logger_clear(struct logger *logger) 228int android_logger_clear(struct logger *logger)
229{ 229{
230 return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); 230 return logger_ioctl(logger, LOGGER_FLUSH_LOG, ANDROID_LOG_WRONLY);
231} 231}
232 232
233/* returns the total size of the log's ring buffer */ 233/* returns the total size of the log's ring buffer */
234long android_logger_get_log_size(struct logger *logger) 234long android_logger_get_log_size(struct logger *logger)
235{ 235{
236 return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); 236 return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, ANDROID_LOG_RDWR);
237} 237}
238 238
239int android_logger_set_log_size(struct logger *logger __unused, 239int android_logger_set_log_size(struct logger *logger __unused,
@@ -248,7 +248,7 @@ int android_logger_set_log_size(struct logger *logger __unused,
248 */ 248 */
249long android_logger_get_log_readable_size(struct logger *logger) 249long android_logger_get_log_readable_size(struct logger *logger)
250{ 250{
251 return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); 251 return logger_ioctl(logger, LOGGER_GET_LOG_LEN, ANDROID_LOG_RDONLY);
252} 252}
253 253
254/* 254/*
@@ -256,7 +256,7 @@ long android_logger_get_log_readable_size(struct logger *logger)
256 */ 256 */
257int android_logger_get_log_version(struct logger *logger) 257int android_logger_get_log_version(struct logger *logger)
258{ 258{
259 int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); 259 int ret = logger_ioctl(logger, LOGGER_GET_VERSION, ANDROID_LOG_RDWR);
260 return (ret < 0) ? 1 : ret; 260 return (ret < 0) ? 1 : ret;
261} 261}
262 262
@@ -342,7 +342,7 @@ struct logger *android_logger_open(struct logger_list *logger_list,
342 goto err_name; 342 goto err_name;
343 } 343 }
344 344
345 logger->fd = open(n, logger_list->mode); 345 logger->fd = open(n, logger_list->mode & (ANDROID_LOG_ACCMODE | ANDROID_LOG_NONBLOCK));
346 if (logger->fd < 0) { 346 if (logger->fd < 0) {
347 goto err_name; 347 goto err_name;
348 } 348 }
@@ -565,7 +565,7 @@ int android_logger_list_read(struct logger_list *logger_list,
565 if (result <= 0) { 565 if (result <= 0) {
566 if (result) { 566 if (result) {
567 error = errno; 567 error = errno;
568 } else if (logger_list->mode & O_NDELAY) { 568 } else if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
569 error = EAGAIN; 569 error = EAGAIN;
570 } else { 570 } else {
571 logger_list->timeout_ms = LOG_TIMEOUT_NEVER; 571 logger_list->timeout_ms = LOG_TIMEOUT_NEVER;
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 9839729a8..29501beb9 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -39,7 +39,7 @@ TEST(libc, __libc_android_log_event_int) {
39 pid_t pid = getpid(); 39 pid_t pid = getpid();
40 40
41 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( 41 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
42 LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); 42 LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
43 43
44 struct timespec ts; 44 struct timespec ts;
45 clock_gettime(CLOCK_MONOTONIC, &ts); 45 clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -99,7 +99,7 @@ TEST(libc, __libc_fatal_no_abort) {
99 pid_t pid = getpid(); 99 pid_t pid = getpid();
100 100
101 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( 101 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
102 (log_id_t)LOG_ID_CRASH, O_RDONLY | O_NDELAY, 1000, pid))); 102 (log_id_t)LOG_ID_CRASH, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
103 103
104 char b[80]; 104 char b[80];
105 struct timespec ts; 105 struct timespec ts;
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 549d79eb4..979adedee 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -130,7 +130,7 @@ static void BM_log_latency(int iters) {
130 pid_t pid = getpid(); 130 pid_t pid = getpid();
131 131
132 struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS, 132 struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS,
133 O_RDONLY, 0, pid); 133 ANDROID_LOG_RDONLY, 0, pid);
134 134
135 if (!logger_list) { 135 if (!logger_list) {
136 fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); 136 fprintf(stderr, "Unable to open events log: %s\n", strerror(errno));
@@ -208,7 +208,7 @@ static void BM_log_delay(int iters) {
208 pid_t pid = getpid(); 208 pid_t pid = getpid();
209 209
210 struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS, 210 struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS,
211 O_RDONLY, 0, pid); 211 ANDROID_LOG_RDONLY, 0, pid);
212 212
213 if (!logger_list) { 213 if (!logger_list) {
214 fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); 214 fprintf(stderr, "Unable to open events log: %s\n", strerror(errno));
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 393e2cd63..33f648101 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -122,7 +122,7 @@ TEST(liblog, __android_log_btwrite__android_logger_list_read) {
122 pid_t pid = getpid(); 122 pid_t pid = getpid();
123 123
124 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( 124 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
125 LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); 125 LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
126 126
127 log_time ts(CLOCK_MONOTONIC); 127 log_time ts(CLOCK_MONOTONIC);
128 128
@@ -223,7 +223,7 @@ TEST(liblog, android_logger_list_read__cpu) {
223 v += pid & 0xFFFF; 223 v += pid & 0xFFFF;
224 224
225 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( 225 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
226 LOG_ID_EVENTS, O_RDONLY, 1000, pid))); 226 LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
227 227
228 int count = 0; 228 int count = 0;
229 229
@@ -443,7 +443,7 @@ TEST(liblog, max_payload) {
443 struct logger_list *logger_list; 443 struct logger_list *logger_list;
444 444
445 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( 445 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
446 LOG_ID_SYSTEM, O_RDONLY, 100, 0))); 446 LOG_ID_SYSTEM, ANDROID_LOG_RDONLY, 100, 0)));
447 447
448 bool matches = false; 448 bool matches = false;
449 ssize_t max_len = 0; 449 ssize_t max_len = 0;
@@ -505,7 +505,7 @@ TEST(liblog, too_big_payload) {
505 struct logger_list *logger_list; 505 struct logger_list *logger_list;
506 506
507 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( 507 ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
508 LOG_ID_SYSTEM, O_RDONLY | O_NDELAY, 100, 0))); 508 LOG_ID_SYSTEM, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 100, 0)));
509 509
510 ssize_t max_len = 0; 510 ssize_t max_len = 0;
511 511
@@ -552,12 +552,12 @@ TEST(liblog, dual_reader) {
552 552
553 // >25 messages due to liblog.__android_log_buf_print__concurrentXX above. 553 // >25 messages due to liblog.__android_log_buf_print__concurrentXX above.
554 ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open( 554 ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open(
555 LOG_ID_MAIN, O_RDONLY | O_NDELAY, 25, 0))); 555 LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 25, 0)));
556 556
557 struct logger_list *logger_list2; 557 struct logger_list *logger_list2;
558 558
559 if (NULL == (logger_list2 = android_logger_list_open( 559 if (NULL == (logger_list2 = android_logger_list_open(
560 LOG_ID_MAIN, O_RDONLY | O_NDELAY, 15, 0))) { 560 LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 15, 0))) {
561 android_logger_list_close(logger_list1); 561 android_logger_list_close(logger_list1);
562 ASSERT_TRUE(NULL != logger_list2); 562 ASSERT_TRUE(NULL != logger_list2);
563 } 563 }
@@ -595,7 +595,7 @@ TEST(liblog, dual_reader) {
595} 595}
596 596
597TEST(liblog, android_logger_get_) { 597TEST(liblog, android_logger_get_) {
598 struct logger_list * logger_list = android_logger_list_alloc(O_WRONLY, 0, 0); 598 struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0);
599 599
600 for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { 600 for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
601 log_id_t id = static_cast<log_id_t>(i); 601 log_id_t id = static_cast<log_id_t>(i);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 79f2ebda2..829a0af88 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -324,7 +324,7 @@ int main(int argc, char **argv)
324 int getPruneList = 0; 324 int getPruneList = 0;
325 char *setPruneList = NULL; 325 char *setPruneList = NULL;
326 int printStatistics = 0; 326 int printStatistics = 0;
327 int mode = O_RDONLY; 327 int mode = ANDROID_LOG_RDONLY;
328 const char *forceFilters = NULL; 328 const char *forceFilters = NULL;
329 log_device_t* devices = NULL; 329 log_device_t* devices = NULL;
330 log_device_t* dev; 330 log_device_t* dev;
@@ -359,15 +359,15 @@ int main(int argc, char **argv)
359 359
360 case 'c': 360 case 'c':
361 clearLog = 1; 361 clearLog = 1;
362 mode = O_WRONLY; 362 mode |= ANDROID_LOG_WRONLY;
363 break; 363 break;
364 364
365 case 'd': 365 case 'd':
366 mode = O_RDONLY | O_NDELAY; 366 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
367 break; 367 break;
368 368
369 case 't': 369 case 't':
370 mode = O_RDONLY | O_NDELAY; 370 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
371 /* FALLTHRU */ 371 /* FALLTHRU */
372 case 'T': 372 case 'T':
373 if (strspn(optarg, "0123456789") != strlen(optarg)) { 373 if (strspn(optarg, "0123456789") != strlen(optarg)) {