1212#include < stdio.h>
1313#include < unistd.h>
1414#include < string.h>
15+ #include < sys/types.h>
16+ #include < dirent.h>
17+ #include < sys/stat.h>
1518
1619
1720using namespace AliceO2 ::InfoLogger;
@@ -21,77 +24,165 @@ void print_usage(){
2124 printf (" Usage: log [options] message(s)\n " );
2225 printf (" Options: \n " );
2326 printf (" -s [severity] Possible values: Info (default), Error, Fatal, Warning, Debug.\n " );
24- printf (" -x If set, reads data coming on stdin line by line\n " );
27+ printf (" -o[key]=[value] Set a message field. Valid keys: context (Facility, Role, System, Detector, Partition, Run) and message options (Severity, Level, ErrorCode, SourceFile, SourceLine).\n " );
28+ printf (" -x If set, reads data coming on stdin line by line.\n " );
2529 printf (" and transmit them as messages (1 line = 1 message).\n " );
2630 printf (" -h This help.\n " );
2731}
2832
2933
3034
35+ // reads an input string option, and sets accordingly msgOptions or msgContext variable.
36+ // returns 0 on success, -1 on error
37+ int processOption (const char *option, InfoLogger::InfoLoggerMessageOption &msgOptions, InfoLoggerContext &msgContext) {
38+ std::string s (option);
39+ size_t separatorPosition=s.find (' =' );
40+ if (separatorPosition==std::string::npos) {return -1 ;}
41+ s[separatorPosition]=0 ;
42+ const char *key=s.c_str ();
43+ const char *value=&(option[separatorPosition+1 ]); // use input string instead of s, so that it's persistent
44+
45+ InfoLoggerContext::FieldName contextField;
46+ if (InfoLoggerContext::getFieldNameFromString (key,contextField)==0 ) {
47+ return msgContext.setField (contextField,value);
48+ } else {
49+ return InfoLogger::setMessageOption (key,value,msgOptions);
50+ }
51+
52+ return 0 ;
53+ }
54+
55+
56+ // find process id on other side of input pipe, if any.
57+ // this may not work if process has already exited.
58+ // returns PID, or zero if not found.
59+ pid_t findPipeProcess () {
60+ pid_t thePid=0 ;
61+
62+ // is stdin a pipe?
63+ pid_t myPid=getpid ();
64+ char stdinPath[PATH_MAX];
65+ snprintf (stdinPath,sizeof (stdinPath)," /proc/%d/fd/0" ,(int )myPid);
66+ struct stat stdinStat;
67+ if (lstat (stdinPath,&stdinStat)==0 ) {
68+ // should be a symlink
69+ if (S_ISLNK (stdinStat.st_mode )) {
70+ char ofd[PATH_MAX];
71+ // read link name
72+ int i=readlink (stdinPath, ofd, sizeof (ofd));
73+ if (i>0 ) {
74+ ofd[i]=0 ; // set NUL-terminated string
75+ // we could check if name looks like: pipe:[231550]
76+ // but let's not restrict, it can work also for sockets, /dev/pts, etc
77+
78+ // browse process list to find other side process
79+ // which has a file descriptor open which is also a symlink, matching pipe name we use as stdin
80+ DIR *dp;
81+ struct dirent *ep;
82+ chdir (" /proc" );
83+ dp = opendir (" ./" );
84+ if (dp != NULL ) {
85+ while ((ep = readdir (dp))) {
86+ int p=0 ;
87+ p=atoi (ep->d_name );
88+ if (p==myPid) {continue ;}
89+ if (p!=0 ) {
90+ DIR *pid_dp;
91+ struct dirent *pid_ep;
92+ struct stat b;
93+ char fp[1024 ];
94+ snprintf (fp,sizeof (fp)," /proc/%d/fd" ,p);
95+ pid_dp=opendir (fp);
96+ if (pid_dp!=NULL ) {
97+ while ((pid_ep = readdir (pid_dp))) {
98+ char fp2[1024 ];
99+ snprintf (fp2,sizeof (fp2)," /proc/%d/fd/%s" ,p,pid_ep->d_name );
100+ if (lstat (fp2,&b)==0 ) {
101+ if (S_ISLNK (b.st_mode )) {
102+ char ofd2[1024 ];
103+ int i;
104+ i=readlink (fp2, ofd2, sizeof (ofd2));
105+ if (i>0 ) {
106+ ofd2[i]=0 ; // set NUL-terminated string
107+ if (!strcmp (ofd2,ofd)) {
108+ thePid=p;
109+ break ;
110+ }
111+ }
112+ }
113+ }
114+ if (thePid) {
115+ break ;
116+ }
117+ }
118+ closedir (pid_dp);
119+ }
120+ }
121+ if (thePid) {
122+ break ;
123+ }
124+ }
125+ closedir (dp);
126+ }
127+ }
128+ }
129+ }
130+ return thePid;
131+ }
132+
133+
134+
135+
136+
31137int main (int argc, char **argv){
32138
33139 int optFromStdin=0 ; // 1 if logging from stdin, 0 when taking input from command line
34140
35- InfoLogger::Severity severity=InfoLogger::Severity::Info;
36-
141+ InfoLogger::InfoLoggerMessageOption msgOptions=InfoLogger::undefinedMessageOption;
142+ msgOptions.severity =InfoLogger::Severity::Info;
143+ InfoLoggerContext msgContext;
144+
145+ // by default, use log context of parent process
146+ msgContext.refresh (getppid ());
147+
37148 char option;
38149
39- /* read options */
40- while ((option = getopt (argc, argv, " s:xh " )) != -1 ){
150+ // read options
151+ while ((option = getopt (argc, argv, " s:xo:h " )) != -1 ){
41152 switch (option){
42153
43154 case ' s' :
44- severity=getSeverityFromString (optarg);
155+ {
156+ InfoLogger::Severity severity=InfoLogger::Severity::Undefined;
157+ severity=InfoLogger::getSeverityFromString (optarg);
45158 if (severity==InfoLogger::Severity::Undefined) {
46159 printf (" Bad severity\n " );
47- print_usage ();
48160 return -1 ;
49161 }
162+ msgOptions.severity =severity;
163+ }
50164 break ;
51165
52166 case ' x' :
53- optFromStdin=1 ;
54- break ;
55-
56- /*
57- // old options
58- case 'f':
59- facility=optarg;
60- break;
61-
62-
63- case 'l':
64- level=infoLogger_getLevelFromString(optarg);
65- if (level==UNDEFINED_INT) {
66- printf("Bad level\n");
67- print_usage();
68- return -1;
167+ {
168+ optFromStdin=1 ;
169+ // try to find process sending messages to stdin
170+ pid_t pid=findPipeProcess ();
171+ if (pid!=0 ) {
172+ msgContext.refresh (pid);
173+ }
69174 }
70175 break ;
71-
72- case 'd':
73- detector=optarg;
74- break;
75-
76- case 'p':
77- partition=optarg;
78- break;
79-
80-
81- case 'c':
82- errcode=0;
83- errcode=atoi(optarg);
84- if (errcode<=0) {
85- printf("Bad error code\n");
86- print_usage();
176+
177+ case ' o' :
178+ if (processOption (optarg,msgOptions,msgContext)) {
179+ printf (" Can not parse option %s\n " ,optarg);
87180 return -1 ;
88181 }
89182 break ;
90183
91- case 'o':
92- extraconfig=optarg;
93- break;
94-
184+ /*
185+ // old options
95186 case 'z':
96187 configFile=optarg;
97188 break;
@@ -109,15 +200,15 @@ int main(int argc, char **argv){
109200
110201 InfoLogger theLog;
111202
112- /* additionnal args = messages to send */
203+ // additionnal args = messages to send
113204 int i;
114205 for (i=optind; i < argc; i++) {
115- theLog.log (severity ," %s" ,argv[i]);
206+ theLog.log (msgOptions,msgContext ," %s" ,argv[i]);
116207 }
117208
118209// todo: catch exceptions
119210
120- /* also read from stdin if option set */
211+ // also read from stdin if option set */
121212 if (optFromStdin) {
122213 LineBuffer lb; // buffer for input lines
123214 std::string msg;
@@ -132,7 +223,7 @@ int main(int argc, char **argv){
132223 break ;
133224 }
134225 // infoLogger_msg_xt(UNDEFINED_STRING,UNDEFINED_INT,UNDEFINED_INT,facility,severity,level,msg);
135- theLog.log (severity ," %s" ,msg.c_str ());
226+ theLog.log (msgOptions,msgContext ," %s" ,msg.c_str ());
136227 }
137228 if (eof) break ;
138229 }
0 commit comments