Browse code

Implement unix domain socket command 'id'. Implement unix domain socket data forwarding when there are several recenteditors editing the same filename and make clients try to start a server when the server closes the connection.

Dario Rodriguez authored on 02/01/2024 19:31:21
Showing 1 changed files
... ...
@@ -23,6 +23,7 @@
23 23
 #include <sys/select.h>
24 24
 #include <stdarg.h>
25 25
 #include <sys/stat.h>
26
+#include <limits.h>
26 27
 
27 28
 #include "re_data.h"
28 29
 #include "re_plugin_unsaved.h"
... ...
@@ -43,6 +44,7 @@
43 44
 #define COMMBUFSIZE 16384
44 45
 #define COMMCLIENTSBLOCKSIZE 32
45 46
 #define SOCKETFILENAMESIZE 1024
47
+#define MAXFILENAMESIZE PATH_MAX+1
46 48
 #define LISTENBACKLOG 5
47 49
 
48 50
 #define VIEWONLYPROGNAME "review"
... ...
@@ -181,6 +183,8 @@ typedef struct theme_t {
181 183
 
182 184
 typedef struct commclient_t {
183 185
         int fd;
186
+        int flag_connectedtoserver;
187
+        char remoteid[MAXFILENAMESIZE];
184 188
         int sizebufin;
185 189
         int usedbufin;
186 190
         int gotbufin;
... ...
@@ -193,6 +197,7 @@ typedef struct commclient_t {
193 197
 
194 198
 typedef struct comms_t {
195 199
         int serverfd;
200
+        char id[MAXFILENAMESIZE];
196 201
         char socketfilename[SOCKETFILENAMESIZE];
197 202
         int sizeclients;
198 203
         int usedclients;
... ...
@@ -294,7 +299,7 @@ int re_wheelaccel(re_t *re, int rawamount);
294 299
 int re_themeset(re_t *re, int ntheme);
295 300
 int re_socketinit(re_t *re, char *filename);
296 301
 void re_socketfree(re_t *re);
297
-int re_socketnewclient(re_t *re);
302
+int re_socketnewclient(re_t *re, int alreadyacceptedfd);
298 303
 int re_socketstep(re_t *re);
299 304
 int re_socketin(re_t *re, int nclient, char *line);
300 305
 #ifndef __GNUC__
... ...
@@ -2982,6 +2987,13 @@ re_socketinit(re_t *re,char *filename)
2982 2987
                 }
2983 2988
                 re->comms.usedclients=0;
2984 2989
         }
2990
+        /* fill the id */
2991
+        if(realpath(filename,re->comms.id)==NULL) {
2992
+                /* if the path cannot be resolved, copy the filename */
2993
+                strncpy(re->comms.id,filename,sizeof(re->comms.id));
2994
+                re->comms.id[sizeof(re->comms.id)-1]='\0';
2995
+        }
2996
+        re->comms.id[sizeof(re->comms.id)-1]='\0';
2985 2997
         /* prepare the new filepath for the socket */
2986 2998
         if((passwd=getpwuid(getuid()))==NULL || (username=passwd->pw_name)==NULL) {
2987 2999
                 fprintf(stderr,"WARNING: Couldn't get username\n");
... ...
@@ -3000,16 +3012,39 @@ re_socketinit(re_t *re,char *filename)
3000 3012
                 return(-1);
3001 3013
         }
3002 3014
         /* detect if there is a stale socket */
3003
-        if((oldfd=open(comms->socketfilename, O_WRONLY|O_APPEND))!=-1) {
3015
+        if((errstr="create")==NULL
3016
+          || (oldfd=socket(AF_UNIX,SOCK_STREAM,0))==-1
3017
+          || (errstr="assing")==NULL /* this one never fails, just for completion */
3018
+          || (memset(&addr,0,sizeof(struct sockaddr_un)))==NULL
3019
+          || (addr.sun_family=AF_UNIX)!=AF_UNIX
3020
+          || strncpy(addr.sun_path,comms->socketfilename,sizeof(addr.sun_path))==NULL
3021
+          || (errstr="connect")==NULL
3022
+          || connect(oldfd,(struct sockaddr *)&addr,sizeof(addr))==-1
3023
+        ) {
3024
+#if 0
3025
+fprintf(stderr,"Check for other server result: %s fail.\n",errstr);
3026
+#endif
3027
+                close(oldfd),oldfd=-1; /* OK: the re is no server as couldn't connect */
3028
+        } else {
3004 3029
                 fd_set writeset;
3005 3030
                 struct timeval tv;
3006 3031
                 FD_ZERO(&writeset);
3007 3032
                 FD_SET(oldfd,&writeset);
3008 3033
                 tv.tv_sec=tv.tv_usec=0;
3009 3034
                 if(select(oldfd+1,NULL,&writeset,NULL,&tv)>0) {
3035
+                        int nclient;
3010 3036
                         comms->socketfilename[0]='\0';
3011
-                        fprintf(stderr,"WARNING: There is a process using the communication socket for the file\n");
3012
-#warning TODO: if the unix domain socket is in use, connect to it and warn that I''m also able to service that filename, so (1) it forwards the petitions, and (2) be waware of when the other process quits to retake control of the unix domain socket
3037
+                        nclient=re_socketnewclient(re,oldfd),oldfd=-1;
3038
+                        if(nclient!=-1) {
3039
+                                re_socketout(re,nclient,"id %s\n",comms->id);
3040
+                                comms->clients[nclient]->flag_connectedtoserver=1;
3041
+#if 0
3042
+fprintf(stderr,"nclient:%i id:\"%s\".\n",nclient,comms->id);
3043
+fprintf(stderr,"Connected to server.\n");
3044
+#endif
3045
+                                return(0);
3046
+                        }
3047
+                        fprintf(stderr,"WARNING: There is a process using the communication socket for the file and couldn't connect to it.\n");
3013 3048
                         return(-1);
3014 3049
                 }
3015 3050
                 close(oldfd),oldfd=-1;
... ...
@@ -3033,7 +3068,7 @@ re_socketinit(re_t *re,char *filename)
3033 3068
                 fprintf(stderr,"WARNING: Couldn't %s unix domain socket\n",errstr);
3034 3069
                 return(-1);
3035 3070
         }
3036
-#if 1
3071
+#if 0
3037 3072
 fprintf(stderr,"Server registered.\n");
3038 3073
 #endif
3039 3074
         return(0);
... ...
@@ -3066,7 +3101,7 @@ re_socketfree(re_t *re)
3066 3101
 }
3067 3102
 
3068 3103
 int
3069
-re_socketnewclient(re_t *re)
3104
+re_socketnewclient(re_t *re, int alreadyacceptedfd)
3070 3105
 {
3071 3106
         int i;
3072 3107
         int fd;
... ...
@@ -3076,7 +3111,10 @@ re_socketnewclient(re_t *re)
3076 3111
         addrlen=sizeof(addr);
3077 3112
         if(re==NULL)
3078 3113
                 return(-1); /* sanity check failed */
3079
-        fd=accept(re->comms.serverfd,&addr,&addrlen);
3114
+        if(alreadyacceptedfd==-1)
3115
+                fd=accept(re->comms.serverfd,&addr,&addrlen);
3116
+        else
3117
+                fd=alreadyacceptedfd;
3080 3118
         if(re->comms.usedclients==re->comms.sizeclients) {
3081 3119
                 commclient_t **newclients;
3082 3120
                 if((newclients=realloc(re->comms.clients,sizeof(commclient_t *)*(re->comms.sizeclients+COMMCLIENTSBLOCKSIZE)))==NULL) {
... ...
@@ -3112,8 +3150,7 @@ re_socketnewclient(re_t *re)
3112 3150
         client->usedbufin=client->gotbufin=0;
3113 3151
         client->usedbufout=client->gotbufout=0;
3114 3152
         re->comms.usedclients++;
3115
-        re_socketout(re, i, "OK\n");
3116
-#if 1
3153
+#if 0
3117 3154
 fprintf(stderr,"New client registered (%i).\n",i);
3118 3155
 #endif
3119 3156
         return(i);
... ...
@@ -3130,13 +3167,16 @@ int re_socketstep(re_t *re)
3130 3167
         int avail,queued;
3131 3168
         int nread,nwritten;
3132 3169
         char *ptr,*next,*end;
3133
-        if(re==NULL || re->comms.serverfd==-1)
3170
+        if(re==NULL)
3134 3171
                 return(-1); /* sanity check error */
3135 3172
         comms=&(re->comms);
3136 3173
         FD_ZERO(&readset);
3137 3174
         FD_ZERO(&writeset);
3138
-        maxfd=comms->serverfd;
3139
-        FD_SET(comms->serverfd,&readset);
3175
+        maxfd=0;
3176
+        if(comms->serverfd!=-1) {
3177
+                maxfd=comms->serverfd;
3178
+                FD_SET(comms->serverfd,&readset);
3179
+        }
3140 3180
         for(i=0;i<comms->sizeclients;i++) {
3141 3181
                 if((client=comms->clients[i])==NULL || client->fd==-1)
3142 3182
                         continue;
... ...
@@ -3158,8 +3198,8 @@ int re_socketstep(re_t *re)
3158 3198
         if(select(maxfd+1,&readset,&writeset,NULL,&tv)<=0)
3159 3199
                 return(0); /* nothing to do */
3160 3200
         /* server */
3161
-        if(FD_ISSET(comms->serverfd,&readset))
3162
-                re_socketnewclient(re);
3201
+        if(comms->serverfd!=-1 && FD_ISSET(comms->serverfd,&readset))
3202
+                re_socketnewclient(re,-1);
3163 3203
         /* clients */
3164 3204
         for(i=0;i<comms->sizeclients;i++) {
3165 3205
                 if((client=comms->clients[i])==NULL || client->fd==-1)
... ...
@@ -3167,16 +3207,20 @@ int re_socketstep(re_t *re)
3167 3207
                 if(FD_ISSET(client->fd,&readset)) {
3168 3208
                         if((queued=sock_queued(client->fd))<=0) {
3169 3209
                                 /* remote has closed the connection */
3170
-#if 1
3210
+#if 0
3171 3211
 fprintf(stderr,"Unregistering client, remote has closed the connection (%i).\n",i);
3172 3212
 #endif
3173 3213
                                 close(client->fd),client->fd=-1;
3214
+                                if(client->flag_connectedtoserver) {
3215
+                                        client->flag_connectedtoserver=0;
3216
+                                        re_socketinit(re,re->filename);
3217
+                                }
3174 3218
                                 continue;
3175 3219
                         }
3176 3220
                         avail=(client->sizebufin-client->usedbufin);
3177 3221
                         queued=(queued>avail)?avail:queued;
3178 3222
                         if((nread=read(client->fd,client->bufin+client->usedbufin,queued))>0) {
3179
-#if 1
3223
+#if 0
3180 3224
 fprintf(stderr,"Read from client %li bytes (%i).\n",(long)nread,i);
3181 3225
 #endif
3182 3226
                                 client->usedbufin+=nread;
... ...
@@ -3203,7 +3247,7 @@ fprintf(stderr,"Read from client %li bytes (%i).\n",(long)nread,i);
3203 3247
                         queued=client->usedbufout-client->gotbufout;
3204 3248
                         if((nwritten=write(client->fd,client->bufout+client->gotbufout,client->usedbufout-client->gotbufout))>0)
3205 3249
                                 client->gotbufout+=nwritten;
3206
-#if 1
3250
+#if 0
3207 3251
 fprintf(stderr,"Write to client %li bytes (%i).\n",(long)nwritten,i);
3208 3252
 #endif
3209 3253
                 }
... ...
@@ -3217,14 +3261,23 @@ re_socketin(re_t *re, int nclient, char *line)
3217 3261
         /* note that the '\n' delimiter has been already removed */
3218 3262
         /* Commands can be sent from the command line using socat as in: */
3219 3263
         /* "socat - UNIX-CONNECT:/tmp/.re_${USER}/filename.ext" */
3264
+        commclient_t *client;
3220 3265
         char *ptr;
3221
-#if 1
3266
+        int i;
3267
+        if(re==NULL || nclient<0 || nclient>=re->comms.sizeclients || re->comms.clients[nclient]==NULL || line==NULL)
3268
+                return(-1); /* sanity check failed */
3269
+        client=re->comms.clients[nclient];
3270
+#if 0
3222 3271
 fprintf(stderr,"Received from client \"%s\" (%i).\n",line,nclient);
3223 3272
 #endif
3224 3273
         if(memcmp(line,"goto ",5)==0) {
3225 3274
                 int oldline;
3275
+                int avail;
3226 3276
                 ptr=line+5;
3227 3277
                 /* change line */
3278
+#if 0
3279
+fprintf(stderr,"Changing line because of command from client \"%s\" (%i).\n",line,nclient);
3280
+#endif
3228 3281
                 re->command=COMMAND_GOTOLINE;
3229 3282
                 strncpy(re->commandbuf,ptr,sizeof(re->commandbuf));
3230 3283
                 re->commandbuf[sizeof(re->commandbuf)-1]='\0';
... ...
@@ -3237,6 +3290,33 @@ fprintf(stderr,"Received from client \"%s\" (%i).\n",line,nclient);
3237 3290
                 }
3238 3291
                 /* send end-of-results */
3239 3292
                 re_socketout(re, nclient, "\n");
3293
+                /* forward command to all identified clients except the one who sent teh command */
3294
+                for(i=0;i<re->comms.sizeclients;i++) {
3295
+#if 0
3296
+                        if(re->comms.clients[i]!=NULL)
3297
+                                fprintf(stderr,"Client[%i] id:\"%s\"\n",i,re->comms.clients[i]->remoteid);
3298
+#endif
3299
+                        if(re->comms.clients[i]==NULL || re->comms.clients[i]->fd==-1 || re->comms.clients[i]->remoteid[0]=='\0')
3300
+                                continue;
3301
+                        avail=re->comms.clients[i]->sizebufout-re->comms.clients[i]->usedbufout;
3302
+                        if((strlen(line)+1)>avail) {
3303
+                                fprintf(stderr,"Couldn't forward command to client because the output buffer is full (avail:%li, req:%li)\n",(long)avail,(long)(strlen(line)+1));
3304
+                                continue;
3305
+                        }
3306
+                        re_socketout(re, i, "%s\n",line);
3307
+                }
3308
+        } else if(memcmp(line,"id ",3)==0) {
3309
+                ptr=line+3;
3310
+                /* change id */
3311
+#if 0
3312
+fprintf(stderr,"Storing client id because of command from client \"%s\" (%i).\n",line,nclient);
3313
+#endif
3314
+                strncpy(client->remoteid,ptr,sizeof(client->remoteid));
3315
+                client->remoteid[sizeof(client->remoteid)-1]='\0';
3316
+                /* send end-of-results */
3317
+                re_socketout(re, nclient, "\n");
3318
+        } else if(line[0]=='\0' ) {
3319
+                ; /* ignore the end-of-message (should only receive them on forwarding clients) */
3240 3320
         } else {
3241 3321
 #if 1
3242 3322
 fprintf(stderr,"Ignoring unknown command from client \"%s\" (%i).\n",line,nclient);