Browse code

Add an unix domain socket to be able to integrate with other tools

Dario Rodriguez authored on 01/01/2024 22:02:37
Showing 1 changed files
... ...
@@ -15,6 +15,14 @@
15 15
 #include <signal.h>
16 16
 #include <time.h>
17 17
 #include <sys/time.h>
18
+#include <sys/socket.h>  /* unix domain socket support */
19
+#include <sys/un.h> /* unix domain socket support */
20
+#include <sys/types.h> /* getpwuid() */
21
+#include <pwd.h> /* getpwuid() */
22
+#include <fcntl.h>
23
+#include <sys/select.h>
24
+#include <stdarg.h>
25
+#include <sys/stat.h>
18 26
 
19 27
 #include "re_data.h"
20 28
 #include "re_plugin_unsaved.h"
... ...
@@ -31,7 +39,11 @@
31 39
 #define COMMANDBUFSIZE 1024
32 40
 #define DEFAULTFONTHEIGHT 14
33 41
 #define SELECTBUFBLOCK 16384
34
-#define SIZEBLOCKPRINTS 32
42
+#define PRINTSBLOCKSIZE 32
43
+#define COMMBUFSIZE 16384
44
+#define COMMCLIENTSBLOCKSIZE 32
45
+#define SOCKETFILENAMESIZE 1024
46
+#define LISTENBACKLOG 5
35 47
 
36 48
 #define VIEWONLYPROGNAME "review"
37 49
 
... ...
@@ -167,6 +179,26 @@ typedef struct theme_t {
167 179
         char color_curline[5];
168 180
 } theme_t;
169 181
 
182
+typedef struct commclient_t {
183
+        int fd;
184
+        int sizebufin;
185
+        int usedbufin;
186
+        int gotbufin;
187
+        char bufin[COMMBUFSIZE];
188
+        int sizebufout;
189
+        int usedbufout;
190
+        int gotbufout;
191
+        char bufout[COMMBUFSIZE];
192
+} commclient_t;
193
+
194
+typedef struct comms_t {
195
+        int serverfd;
196
+        char socketfilename[SOCKETFILENAMESIZE];
197
+        int sizeclients;
198
+        int usedclients;
199
+        commclient_t **clients;
200
+} comms_t;
201
+
170 202
 typedef struct re_t {
171 203
         redata_t *data;
172 204
         reui_t *ui;
... ...
@@ -210,6 +242,7 @@ typedef struct re_t {
210 242
         int originlinefunclisting;
211 243
         int curlinefunclisting;
212 244
         struct timeval lastwheel;
245
+        comms_t comms;
213 246
 } re_t;
214 247
 
215 248
 volatile int flag_sigint;
... ...
@@ -259,7 +292,16 @@ redata_t *re_getfunclisting(re_t *re);
259 292
 int re_funclistingxy2line(re_t *re,int mx,int my);
260 293
 int re_wheelaccel(re_t *re, int rawamount);
261 294
 int re_themeset(re_t *re, int ntheme);
262
-
295
+int re_socketinit(re_t *re, char *filename);
296
+void re_socketfree(re_t *re);
297
+int re_socketnewclient(re_t *re);
298
+int re_socketstep(re_t *re);
299
+int re_socketin(re_t *re, int nclient, char *line);
300
+#ifndef __GNUC__
301
+int re_socketout(re_t *re, int nclient, char *format, ...);
302
+#else
303
+        int re_socketout(re_t *re, int nclient, char *format, ...) __attribute__((format(printf,3,4)));
304
+#endif
263 305
 
264 306
 int
265 307
 main(int argc, char *argv[])
... ...
@@ -341,6 +383,9 @@ fprintf(stderr,"QUESTION INIT: %s: %s\n",re->question->title,re->question->body)
341 383
                                         re->commandbuf[sizeof(re->commandbuf)-1]='\0';
342 384
                                         redata_needssaving_reset(re->data);
343 385
                                 }
386
+                                if(re_socketinit(re,re->filename)!=0) {
387
+                                        fprintf(stderr,"WARNING: Couldn't init communication socket; there will be no integration with other tools\n");
388
+                                }
344 389
                         }
345 390
                 }
346 391
                 /* workaround for some nvidia linux drivers, that show old data on partial updates */
... ...
@@ -396,6 +441,7 @@ fprintf(stderr,"RENDER\n");
396 441
                                 reui_present(re->prints[i].ui);
397 442
                 }
398 443
                 sselect_wait(ssel,(flag_had_events)?10:100);
444
+                re_socketstep(re);
399 445
                 flag_had_events=(flag_had_events>0)?flag_had_events-1:0;
400 446
                 SDL_PumpEvents();
401 447
                 while(SDL_PeepEvents(&event,1,SDL_GETEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT)>0) {
... ...
@@ -874,6 +920,7 @@ re_free(re_t *re)
874 920
                 redata_free(re->data),re->data=NULL;
875 921
         if(re->selectbuf!=NULL)
876 922
                 free(re->selectbuf),re->selectbuf=NULL,re->usedselectbuf=re->sizeselectbuf=0;
923
+        re_socketfree(re);
877 924
         free(re),re=NULL;
878 925
         return;
879 926
 }
... ...
@@ -2172,11 +2219,11 @@ re_addprint(re_t *re)
2172 2219
         /* ensure space for the new printout */
2173 2220
         if(re->usedprints==re->sizeprints) {
2174 2221
                 printout_t *newprints;
2175
-                if((newprints=realloc(re->prints,sizeof(printout_t)*(re->sizeprints+SIZEBLOCKPRINTS)))==NULL)
2222
+                if((newprints=realloc(re->prints,sizeof(printout_t)*(re->sizeprints+PRINTSBLOCKSIZE)))==NULL)
2176 2223
                         return(-1); /* insuf. mem. */
2177 2224
                 re->prints=newprints;
2178
-                memset(re->prints+re->sizeprints,0,sizeof(printout_t)*SIZEBLOCKPRINTS);
2179
-                re->sizeprints+=SIZEBLOCKPRINTS;
2225
+                memset(re->prints+re->sizeprints,0,sizeof(printout_t)*PRINTSBLOCKSIZE);
2226
+                re->sizeprints+=PRINTSBLOCKSIZE;
2180 2227
         }
2181 2228
         for(i=0;i<re->sizeprints;i++) {
2182 2229
                 if(re->prints[i].ui==NULL)
... ...
@@ -2897,3 +2944,297 @@ re_themeset(re_t *re, int ntheme)
2897 2944
         return(0);
2898 2945
 }
2899 2946
 
2947
+int
2948
+re_socketinit(re_t *re,char *filename)
2949
+{
2950
+        int i;
2951
+        comms_t *comms;
2952
+        commclient_t *client;
2953
+        struct sockaddr_un addr;
2954
+        char *ptr;
2955
+        struct passwd *passwd;
2956
+        char *username;
2957
+        int oldfd;
2958
+        char *errstr;
2959
+        if(re==NULL)
2960
+                return(-1);
2961
+        comms=&(re->comms);
2962
+        /* close all current comms */
2963
+        if(comms->serverfd!=-1) {
2964
+                close(comms->serverfd),comms->serverfd=-1;
2965
+                if(comms->socketfilename[0]!='\0')
2966
+                        unlink(comms->socketfilename),comms->socketfilename[0]='\0';
2967
+        }
2968
+        if(re->comms.clients!=NULL) {
2969
+                for(i=0;i<re->comms.sizeclients;i++) {
2970
+                        if((client=re->comms.clients[i])==NULL)
2971
+                                continue;
2972
+                        if(client->fd!=-1)
2973
+                                close(client->fd),client->fd=-1;
2974
+                }
2975
+                re->comms.usedclients=0;
2976
+        }
2977
+        /* prepare the new filepath for the socket */
2978
+        if((passwd=getpwuid(getuid()))==NULL || (username=passwd->pw_name)==NULL) {
2979
+                fprintf(stderr,"WARNING: Couldn't get username\n");
2980
+                return(-1);
2981
+        }
2982
+        ptr=strrchr(filename,'/');
2983
+        ptr=(ptr!=NULL)?ptr+1:filename;
2984
+        snprintf(comms->socketfilename,sizeof(comms->socketfilename),"/tmp/.re_%s",username);
2985
+        comms->socketfilename[sizeof(comms->socketfilename)-1]='\0';
2986
+        mkdir(comms->socketfilename,0700);
2987
+        snprintf(comms->socketfilename,sizeof(comms->socketfilename),"/tmp/.re_%s/%s",username,ptr);
2988
+        comms->socketfilename[sizeof(comms->socketfilename)-1]='\0';
2989
+        if(strlen(comms->socketfilename)>=(sizeof(addr.sun_path))) {
2990
+                comms->socketfilename[0]='\0';
2991
+                fprintf(stderr,"WARNING: Socket path filename too long\n");
2992
+                return(-1);
2993
+        }
2994
+        /* detect if there is a stale socket */
2995
+        if((oldfd=open(comms->socketfilename, O_WRONLY|O_APPEND))!=-1) {
2996
+                fd_set writeset;
2997
+                struct timeval tv;
2998
+                FD_ZERO(&writeset);
2999
+                FD_SET(oldfd,&writeset);
3000
+                tv.tv_sec=tv.tv_usec=0;
3001
+                if(select(oldfd+1,NULL,&writeset,NULL,&tv)>0) {
3002
+                        comms->socketfilename[0]='\0';
3003
+                        fprintf(stderr,"WARNING: There is a process using the communication socket for the file\n");
3004
+                        return(-1);
3005
+                }
3006
+                close(oldfd),oldfd=-1;
3007
+        }
3008
+        unlink(comms->socketfilename);
3009
+        /* create and bind socket */
3010
+        if((errstr="create")==NULL
3011
+          || (comms->serverfd=socket(AF_UNIX,SOCK_STREAM,0))==-1
3012
+          || (errstr="assing")==NULL /* this one never fails, just for completion */
3013
+          || (memset(&addr,0,sizeof(struct sockaddr_un)))==NULL
3014
+          || (addr.sun_family=AF_UNIX)!=AF_UNIX
3015
+          || strncpy(addr.sun_path,comms->socketfilename,sizeof(addr.sun_path))==NULL
3016
+          || (errstr="bind")==NULL
3017
+          || bind(comms->serverfd,(struct sockaddr *)&addr,sizeof(addr))==-1
3018
+          || (errstr="listen")==NULL
3019
+          || listen(comms->serverfd,LISTENBACKLOG)==-1
3020
+          ) {
3021
+                comms->socketfilename[0]='\0';
3022
+                if(comms->serverfd!=-1)
3023
+                        close(comms->serverfd),comms->serverfd=-1;
3024
+                fprintf(stderr,"WARNING: Couldn't %s unix domain socket\n",errstr);
3025
+                return(-1);
3026
+        }
3027
+#if 1
3028
+fprintf(stderr,"Server registered.\n");
3029
+#endif
3030
+        return(0);
3031
+}
3032
+
3033
+void
3034
+re_socketfree(re_t *re)
3035
+{
3036
+        int i;
3037
+        commclient_t *client;
3038
+        if(re==NULL)
3039
+                return; /* nothing to do */
3040
+        if(re->comms.serverfd!=-1)
3041
+                close(re->comms.serverfd),re->comms.serverfd=-1;
3042
+        if(re->comms.socketfilename[0]!='\0')
3043
+                unlink(re->comms.socketfilename),re->comms.socketfilename[0]='\0';
3044
+        if(re->comms.clients!=NULL) {
3045
+                for(i=0;i<re->comms.sizeclients;i++) {
3046
+                        if((client=re->comms.clients[i])==NULL)
3047
+                                continue;
3048
+                        if(client->fd!=-1)
3049
+                                close(client->fd),client->fd=-1;
3050
+                        free(client),client=NULL;
3051
+                        re->comms.clients[i]=NULL;
3052
+                }
3053
+                re->comms.usedclients=re->comms.sizeclients=0;
3054
+                free(re->comms.clients),re->comms.clients=NULL;
3055
+        }
3056
+        return;
3057
+}
3058
+
3059
+int
3060
+re_socketnewclient(re_t *re)
3061
+{
3062
+        int i;
3063
+        int fd;
3064
+        commclient_t *client;
3065
+        struct sockaddr addr;
3066
+        socklen_t addrlen;
3067
+        addrlen=sizeof(addr);
3068
+        if(re==NULL)
3069
+                return(-1); /* sanity check failed */
3070
+        fd=accept(re->comms.serverfd,&addr,&addrlen);
3071
+        if(re->comms.usedclients==re->comms.sizeclients) {
3072
+                commclient_t **newclients;
3073
+                if((newclients=realloc(re->comms.clients,sizeof(commclient_t *)*(re->comms.sizeclients+COMMCLIENTSBLOCKSIZE)))==NULL) {
3074
+                        close(fd),fd=-1;
3075
+                        return(-1); /* insuf. mem. */
3076
+                }
3077
+                re->comms.clients=newclients;
3078
+                memset(re->comms.clients+re->comms.sizeclients,0,sizeof(commclient_t *)*COMMCLIENTSBLOCKSIZE);
3079
+                re->comms.sizeclients+=COMMCLIENTSBLOCKSIZE;
3080
+        }
3081
+        for(i=0;i<re->comms.sizeclients;i++) {
3082
+                if(re->comms.clients[i]==NULL || re->comms.clients[i]->fd==-1)
3083
+                        break;
3084
+        }
3085
+        if(i>=re->comms.sizeclients) {
3086
+                close(fd),fd=-1;
3087
+                re->comms.usedclients=re->comms.sizeclients;
3088
+                return(-1); /* INTERNAL ERROR */
3089
+        }
3090
+        client=re->comms.clients[i];
3091
+        if(client==NULL) {
3092
+                if((client=malloc(sizeof(commclient_t)))==NULL) {
3093
+                        close(fd),fd=-1;
3094
+                        return(-1); /* INTERNAL ERROR */
3095
+                }
3096
+                re->comms.clients[i]=client;
3097
+                memset(client,0,sizeof(commclient_t));
3098
+                client->fd=-1;
3099
+                client->sizebufin=sizeof(client->bufin);
3100
+                client->sizebufout=sizeof(client->bufout);
3101
+        }
3102
+        client->fd=fd;
3103
+        client->usedbufin=client->gotbufin=0;
3104
+        client->usedbufout=client->gotbufout=0;
3105
+        re->comms.usedclients++;
3106
+        re_socketout(re, i, "OK\n");
3107
+#if 1
3108
+fprintf(stderr,"New client registered (%i).\n",i);
3109
+#endif
3110
+        return(i);
3111
+}
3112
+
3113
+int re_socketstep(re_t *re)
3114
+{
3115
+        int maxfd;
3116
+        fd_set readset,writeset;
3117
+        struct timeval tv;
3118
+        int i;
3119
+        comms_t *comms;
3120
+        commclient_t *client;
3121
+        int avail,queued;
3122
+        int nread,nwritten;
3123
+        char *ptr,*next,*end;
3124
+        if(re==NULL || re->comms.serverfd==-1)
3125
+                return(-1); /* sanity check error */
3126
+        comms=&(re->comms);
3127
+        FD_ZERO(&readset);
3128
+        FD_ZERO(&writeset);
3129
+        maxfd=comms->serverfd;
3130
+        FD_SET(comms->serverfd,&readset);
3131
+        for(i=0;i<comms->sizeclients;i++) {
3132
+                if((client=comms->clients[i])==NULL || client->fd==-1)
3133
+                        continue;
3134
+                if(client->gotbufin>0) {
3135
+                        memmove(client->bufin,client->bufin+client->gotbufin,client->usedbufin-client->gotbufin);
3136
+                        client->usedbufin-=client->gotbufin;
3137
+                        client->gotbufin=0;
3138
+                }
3139
+                if((client->sizebufin-client->usedbufin)>0) {
3140
+                        FD_SET(client->fd,&readset);
3141
+                        maxfd=(maxfd<client->fd)?client->fd:maxfd;
3142
+                }
3143
+                if((client->usedbufout-client->gotbufout)>0) {
3144
+                        FD_SET(client->fd,&writeset);
3145
+                        maxfd=(maxfd<client->fd)?client->fd:maxfd;
3146
+                }
3147
+        }
3148
+        tv.tv_sec=tv.tv_usec=0;
3149
+        if(select(maxfd+1,&readset,&writeset,NULL,&tv)<=0)
3150
+                return(0); /* nothing to do */
3151
+        /* server */
3152
+        if(FD_ISSET(comms->serverfd,&readset))
3153
+                re_socketnewclient(re);
3154
+        /* clients */
3155
+        for(i=0;i<comms->sizeclients;i++) {
3156
+                if((client=comms->clients[i])==NULL || client->fd==-1)
3157
+                        continue;
3158
+                if(FD_ISSET(client->fd,&readset)) {
3159
+                        if((queued=sock_queued(client->fd))<=0) {
3160
+                                /* remote has closed the connection */
3161
+#if 1
3162
+fprintf(stderr,"Unregistering client, remote has closed the connection (%i).\n",i);
3163
+#endif
3164
+                                close(client->fd),client->fd=-1;
3165
+                                continue;
3166
+                        }
3167
+                        avail=(client->sizebufin-client->usedbufin);
3168
+                        queued=(queued>avail)?avail:queued;
3169
+                        if((nread=read(client->fd,client->bufin+client->usedbufin,queued))>0) {
3170
+#if 1
3171
+fprintf(stderr,"Read from client %li bytes (%i).\n",(long)nread,i);
3172
+#endif
3173
+                                client->usedbufin+=nread;
3174
+                                for(ptr=client->bufin+client->gotbufin
3175
+                                   ,end=client->bufin+client->usedbufin
3176
+                                   ,next=memchr(ptr,'\n',end-ptr)
3177
+                                   ,next=(next==NULL)?end:next
3178
+                                  ;client->fd!=-1 && next<end
3179
+                                  ;ptr=(next!=end)?next+1:end
3180
+                                   ,next=memchr(ptr,'\n',end-ptr)
3181
+                                   ,next=(next==NULL)?end:next
3182
+                                  ) {
3183
+                                        *next='\0';
3184
+                                        if(next>ptr && next[-1]=='\r')
3185
+                                                next[-1]='\0';
3186
+                                        re_socketin(re,i,ptr);
3187
+                                }
3188
+                                client->gotbufin=ptr-client->bufin;
3189
+                                if(client->fd==-1)
3190
+                                        continue; /* in case the socket has been closed inside re_socketin() */
3191
+                        }
3192
+                }
3193
+                if(FD_ISSET(client->fd,&writeset)) {
3194
+                        queued=client->usedbufout-client->gotbufout;
3195
+                        if((nwritten=write(client->fd,client->bufout+client->gotbufout,client->usedbufout-client->gotbufout))>0)
3196
+                                client->gotbufout+=nwritten;
3197
+#if 1
3198
+fprintf(stderr,"Write to client %li bytes (%i).\n",(long)nwritten,i);
3199
+#endif
3200
+                }
3201
+        }
3202
+        return(0);
3203
+}
3204
+
3205
+int
3206
+re_socketin(re_t *re, int nclient, char *line)
3207
+{
3208
+        /* note that the '\n' delimiter has been already removed */
3209
+#warning TODO
3210
+#warning Test with "socat - UNIX-CONNECT:/tmp/.re_${USER}/filename.ext"
3211
+#if 1
3212
+
3213
+fprintf(stderr,"Received from client \"%s\" (%i).\n",line,nclient);
3214
+#endif
3215
+        return(0);
3216
+}
3217
+
3218
+int
3219
+re_socketout(re_t *re, int nclient, char *format, ...)
3220
+{
3221
+        /* note that the caller has to output the '\n' delimiter to mark the end of the line */
3222
+        commclient_t *client;
3223
+        va_list mylist;
3224
+        int avail;
3225
+        int res;
3226
+        if(re==NULL || nclient<0 || nclient>=re->comms.sizeclients || re->comms.clients[nclient]==NULL || re->comms.clients[nclient]->fd==-1)
3227
+                return(-1); /* sanity check error */
3228
+        client=re->comms.clients[nclient];
3229
+        memmove(client->bufout,client->bufout+client->gotbufout,client->usedbufout-client->gotbufout);
3230
+        client->usedbufout-=client->gotbufout;
3231
+        client->gotbufout=0;
3232
+        avail=client->sizebufout-client->usedbufout;
3233
+        va_start(mylist,format);
3234
+        res=vsnprintf(client->bufout+client->usedbufout,avail,format,mylist);
3235
+        va_end(mylist);
3236
+        if(res<0 || res>=avail)
3237
+                return(-1); /* doesn't fit or another error */
3238
+        client->usedbufout+=res;
3239
+        return(0);
3240
+}