Coverage Report

Created: 2026-09-28 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/distribute-cache.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%    DDDD    IIIII   SSSSS  TTTTT  RRRR   IIIII  BBBB   U   U  TTTTT  EEEEE   %
6
%    D   D     I     SS       T    R   R    I    B   B  U   U    T    E       %
7
%    D   D     I      SSS     T    RRRR     I    BBBB   U   U    T    EEE     %
8
%    D   D     I        SS    T    R R      I    B   B  U   U    T    E       %
9
%    DDDDA   IIIII   SSSSS    T    R  R   IIIII  BBBB    UUU     T    EEEEE   %
10
%                                                                             %
11
%                      CCCC   AAA    CCCC  H   H  EEEEE                       %
12
%                     C      A   A  C      H   H  E                           %
13
%                     C      AAAAA  C      HHHHH  EEE                         %
14
%                     C      A   A  C      H   H  E                           %
15
%                      CCCC  A   A   CCCC  H   H  EEEEE                       %
16
%                                                                             %
17
%                                                                             %
18
%                 MagickCore Distributed Pixel Cache Methods                  %
19
%                                                                             %
20
%                              Software Design                                %
21
%                                   Cristy                                    %
22
%                                January 2013                                 %
23
%                                                                             %
24
%                                                                             %
25
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
26
%  dedicated to making software imaging solutions freely available.           %
27
%                                                                             %
28
%  You may not use this file except in compliance with the License.  You may  %
29
%  obtain a copy of the License at                                            %
30
%                                                                             %
31
%    https://imagemagick.org/license/                                         %
32
%                                                                             %
33
%  Unless required by applicable law or agreed to in writing, software        %
34
%  distributed under the License is distributed on an "AS IS" BASIS,          %
35
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
36
%  See the License for the specific language governing permissions and        %
37
%  limitations under the License.                                             %
38
%                                                                             %
39
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40
%
41
% A distributed pixel cache is an extension of the traditional pixel cache
42
% available on a single host.  The distributed pixel cache may span multiple
43
% servers so that it can grow in size and transactional capacity to support
44
% very large images.  Start up the pixel cache server on one or more machines.
45
% When you read or operate on an image and the local pixel cache resources are
46
% exhausted, ImageMagick contacts one or more of these remote pixel servers to
47
% store or retrieve pixels.
48
%
49
*/
50

51
/*
52
  Include declarations.
53
*/
54
#include "MagickCore/studio.h"
55
#include "MagickCore/cache.h"
56
#include "MagickCore/cache-private.h"
57
#include "MagickCore/distribute-cache.h"
58
#include "MagickCore/distribute-cache-private.h"
59
#include "MagickCore/exception.h"
60
#include "MagickCore/exception-private.h"
61
#include "MagickCore/geometry.h"
62
#include "MagickCore/image.h"
63
#include "MagickCore/image-private.h"
64
#include "MagickCore/list.h"
65
#include "MagickCore/locale_.h"
66
#include "MagickCore/memory_.h"
67
#include "MagickCore/nt-base-private.h"
68
#include "MagickCore/pixel.h"
69
#include "MagickCore/policy.h"
70
#include "MagickCore/random_.h"
71
#include "MagickCore/registry.h"
72
#include "MagickCore/splay-tree.h"
73
#include "MagickCore/string_.h"
74
#include "MagickCore/string-private.h"
75
#include "MagickCore/utility-private.h"
76
#include "MagickCore/version.h"
77
#include "MagickCore/version-private.h"
78
#define SOCKET_TYPE int
79
#undef MAGICKCORE_HAVE_DISTRIBUTE_CACHE
80
#if defined(MAGICKCORE_DPC_SUPPORT)
81
#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
82
#include <netinet/in.h>
83
#include <netdb.h>
84
#include <sys/socket.h>
85
#include <arpa/inet.h>
86
#define CLOSE_SOCKET(socket) (void) close_utf8(socket)
87
#define HANDLER_RETURN_TYPE void *
88
#define HANDLER_RETURN_VALUE (void *) NULL
89
#define SOCKET_TYPE int
90
#define LENGTH_TYPE size_t
91
#define MAGICKCORE_HAVE_DISTRIBUTE_CACHE 1
92
#elif defined(_MSC_VER)
93
#define CLOSE_SOCKET(socket) (void) closesocket(socket)
94
#define HANDLER_RETURN_TYPE DWORD WINAPI
95
#define HANDLER_RETURN_VALUE 0
96
#define LENGTH_TYPE int
97
#define MAGICKCORE_HAVE_DISTRIBUTE_CACHE 1
98
#define MAGICKCORE_HAVE_WINSOCK2 1
99
#endif
100
#endif
101

102
/*
103
  Define declarations.
104
*/
105
0
#define DPCHostname  "127.0.0.1"
106
#define DPCMaxClientWorkers  128
107
#define DPCMaxUnauthenticatedClientWorkers  16
108
#define DPCPendingConnections  10
109
0
#define DPCPort  6668
110
#define DPCSessionKeyLength  16
111
#ifndef MSG_NOSIGNAL
112
#  define MSG_NOSIGNAL 0
113
#endif
114

115
/*
116
  Static declarations.
117
*/
118
#ifdef MAGICKCORE_HAVE_WINSOCK2
119
static SemaphoreInfo
120
  *winsock_semaphore = (SemaphoreInfo *) NULL;
121
122
static WSADATA
123
  *wsaData = (WSADATA*) NULL;
124
#endif
125
126
#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
127
static SemaphoreInfo
128
  *dpc_semaphore = (SemaphoreInfo *) NULL;
129
130
static char
131
  *dpc_shared_secret = (char *) NULL;
132
133
static size_t
134
  dpc_clients = 0,
135
  dpc_unauthenticated_clients = 0;
136
#endif
137

138
/*
139
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140
%                                                                             %
141
%                                                                             %
142
%                                                                             %
143
+   A c q u i r e D i s t r i b u t e C a c h e I n f o                       %
144
%                                                                             %
145
%                                                                             %
146
%                                                                             %
147
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148
%
149
%  AcquireDistributeCacheInfo() allocates the DistributeCacheInfo structure.
150
%
151
%  The format of the AcquireDistributeCacheInfo method is:
152
%
153
%      DistributeCacheInfo *AcquireDistributeCacheInfo(ExceptionInfo *exception)
154
%
155
%  A description of each parameter follows:
156
%
157
%    o exception: return any errors or warnings in this structure.
158
%
159
*/
160
161
#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
162
static inline MagickOffsetType dpc_receive(SOCKET_TYPE magick_unused(file),
163
  const MagickSizeType magick_unused(length),
164
  unsigned char *magick_restrict magick_unused(message))
165
0
{
166
0
  magick_unreferenced(file);
167
0
  magick_unreferenced(length);
168
0
  magick_unreferenced(message);
169
0
  return(-1);
170
0
}
171
172
static inline MagickOffsetType dpc_receive_deadline(SOCKET_TYPE
173
  magick_unused(file),const MagickSizeType magick_unused(length),
174
  unsigned char *magick_restrict magick_unused(message),
175
  double magick_unused(deadline))
176
0
{
177
0
  magick_unreferenced(file);
178
0
  magick_unreferenced(length);
179
0
  magick_unreferenced(message);
180
0
  magick_unreferenced(deadline);
181
0
  return(-1);
182
0
}
183
#else
184
static void SetSocketReceiveTimeout(SOCKET_TYPE file,double seconds)
185
{
186
  /*
187
    Set a receive timeout.
188
  */
189
#if defined(MAGICKCORE_HAVE_WINSOCK2)
190
  {
191
    DWORD timeout = (DWORD) (seconds*1000.0);
192
    (void) setsockopt(file,SOL_SOCKET,SO_RCVTIMEO,
193
      (const char *) &timeout,sizeof(timeout));
194
  }
195
#else
196
  {
197
    struct timeval tv;
198
    tv.tv_sec=(int) seconds;
199
    tv.tv_usec=(int) ((seconds-(double) tv.tv_sec)*1000000.0);
200
    (void) setsockopt(file,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));
201
  }
202
#endif
203
}
204
205
static inline MagickOffsetType dpc_receive(SOCKET_TYPE file,
206
  const MagickSizeType length,unsigned char *magick_restrict message)
207
{
208
  MagickOffsetType offset = 0;
209
  while (offset < (MagickOffsetType) length)
210
  {
211
    MagickSizeType chunk = MagickMin(length-offset,(MagickSizeType)
212
      MagickMaxBufferExtent);
213
    ssize_t count = recv(file,(char *) message+offset,(LENGTH_TYPE) chunk,0);
214
    if (count > 0)
215
      offset+=(MagickOffsetType) count;
216
    else
217
      if (count == 0)
218
        return(0);
219
      else
220
        {
221
#ifdef MAGICKCORE_HAVE_WINSOCK2
222
          int wsa_err = WSAGetLastError();
223
          if ((wsa_err == WSAEINTR) || (wsa_err == WSAEWOULDBLOCK))
224
            continue;
225
          if (wsa_err == WSAECONNRESET)
226
            return(-2);
227
#else
228
          if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
229
            continue;
230
          if (errno == ECONNRESET)
231
            return(-2);
232
#endif
233
          return(-1);
234
        }
235
  }
236
  return(offset);
237
}
238
239
static inline MagickOffsetType dpc_receive_deadline(SOCKET_TYPE file,
240
  MagickSizeType length,unsigned char *message,double deadline)
241
{
242
  /*
243
    Enforce an absolute deadline for authentication and command reads.
244
  */
245
  MagickOffsetType offset = 0;
246
  while (offset < (MagickOffsetType) length)
247
  {
248
    double
249
      now = (double) time((time_t *) NULL),
250
      remaining = deadline-now;
251
252
    MagickSizeType
253
      chunk;
254
255
    ssize_t
256
      count;
257
258
    if (now >= deadline)
259
      return(-1);
260
    if (remaining < 0.001)
261
      remaining=0.001;
262
    SetSocketReceiveTimeout(file,remaining);
263
    chunk=MagickMin(length-offset,(MagickSizeType) MagickMaxBufferExtent);
264
    count=recv(file,(char *) message+offset,(LENGTH_TYPE) chunk,0);
265
    if (count > 0)
266
      offset+=(MagickOffsetType) count;
267
    else
268
      if (count == 0)
269
        return(0);
270
      else
271
        {
272
#ifdef MAGICKCORE_HAVE_WINSOCK2
273
          int wsa_err = WSAGetLastError();
274
          if ((wsa_err == WSAEINTR) || (wsa_err == WSAEWOULDBLOCK))
275
            continue;
276
          if (wsa_err == WSAECONNRESET)
277
            return(-2);
278
          return(-1);
279
#else
280
          if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
281
            continue;
282
          if (errno == ECONNRESET)
283
            return(-2);
284
          return(-1);
285
#endif
286
        }
287
  }
288
  return(offset);
289
}
290
#endif
291
292
#if defined(MAGICKCORE_HAVE_WINSOCK2)
293
static void InitializeWinsock2(MagickBooleanType use_lock)
294
{
295
  if (use_lock != MagickFalse)
296
    {
297
      if (winsock_semaphore == (SemaphoreInfo *) NULL)
298
        ActivateSemaphoreInfo(&winsock_semaphore);
299
      LockSemaphoreInfo(winsock_semaphore);
300
    }
301
  if (wsaData == (WSADATA *) NULL)
302
    {
303
      wsaData=(WSADATA *) AcquireMagickMemory(sizeof(WSADATA));
304
      if (WSAStartup(MAKEWORD(2,2),wsaData) != 0)
305
        ThrowFatalException(CacheFatalError,"WSAStartup failed");
306
    }
307
  if (use_lock != MagickFalse)
308
    UnlockSemaphoreInfo(winsock_semaphore);
309
}
310
#endif
311
312
#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
313
static int ConnectPixelCacheServer(const char *magick_unused(hostname),
314
  const int magick_unused(port),uint64_t *magick_unused(session_key),
315
  ExceptionInfo *exception)
316
0
{
317
0
  magick_unreferenced(hostname);
318
0
  magick_unreferenced(port);
319
0
  magick_unreferenced(session_key);
320
0
  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
321
0
    "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
322
0
  return(MagickFalse);
323
0
}
324
#else
325
static inline uint64_t ROTL(uint64_t x,int b)
326
{
327
  return((x << b) | (x >> (64-b)));
328
}
329
330
static inline uint64_t U8TO64_LE(const uint8_t *p)
331
{
332
  return(((uint64_t) p[0] << 0)  | ((uint64_t) p[1] << 8)  |
333
         ((uint64_t) p[2] << 16) | ((uint64_t) p[3] << 24) |
334
         ((uint64_t) p[4] << 32) | ((uint64_t) p[5] << 40) |
335
         ((uint64_t) p[6] << 48) | ((uint64_t) p[7] << 56));
336
}
337
338
static inline uint64_t SIPHash24(const uint8_t key[16],const uint8_t *message,
339
  const size_t length)
340
{
341
  const uint8_t
342
    *end = message+length-(length % 8);
343
344
  size_t
345
    i;
346
347
  uint64_t
348
    b = ((uint64_t) length) << 56,
349
    k0 = U8TO64_LE(key),
350
    k1 = U8TO64_LE(key+8),
351
    m,
352
    v0 = 0x736f6d6570736575ULL ^ k0,
353
    v1 = 0x646f72616e646f6dULL ^ k1,
354
    v2 = 0x6c7967656e657261ULL ^ k0,
355
    v3 = 0x7465646279746573ULL ^ k1;
356
357
  for ( ; message != end; message+=8)
358
  {
359
    m=U8TO64_LE(message);
360
    v3^=m;
361
    for (i=0; i < 2; i++)
362
    {
363
      v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
364
      v2+=v3; v3=ROTL(v3,16); v3^=v2;
365
      v0+=v3; v3=ROTL(v3,21); v3^=v0;
366
      v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
367
    }
368
    v0^=m;
369
  }
370
  switch (length & 0x07)
371
  {
372
    case 7: b|=((uint64_t) message[6]) << 48; magick_fallthrough;
373
    case 6: b|=((uint64_t) message[5]) << 40; magick_fallthrough;
374
    case 5: b|=((uint64_t) message[4]) << 32; magick_fallthrough;
375
    case 4: b|=((uint64_t) message[3]) << 24; magick_fallthrough;
376
    case 3: b|=((uint64_t) message[2]) << 16; magick_fallthrough;
377
    case 2: b|=((uint64_t) message[1]) << 8; magick_fallthrough;
378
    case 1: b|=((uint64_t) message[0]); magick_fallthrough;
379
    default: break;
380
  }
381
  v3^=b;
382
  for (i=0; i < 2; i++)
383
  {
384
    v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
385
    v2+=v3; v3=ROTL(v3,16); v3^=v2;
386
    v0+=v3; v3=ROTL(v3,21); v3^=v0;
387
    v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
388
  }
389
  v0^=b;
390
  v2^=0xff;
391
  for (i=0; i < 4; i++)
392
  {
393
    v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
394
    v2+=v3; v3=ROTL(v3,16); v3^=v2;
395
    v0+=v3; v3=ROTL(v3,21); v3^=v0;
396
    v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
397
  }
398
  return(v0^v1^v2^v3);
399
}
400
401
static inline void DeriveSIPKeyFromSecret(const char *shared_secret,
402
  uint8_t key[16])
403
{
404
  size_t
405
    i,
406
    length;
407
408
  uint64_t
409
    k0 = 0x0706050403020100ULL,
410
    k1 = 0x0f0e0d0c0b0a0908ULL;
411
412
  length=strlen(shared_secret);
413
  for (i=0; i < length; i++)
414
  {
415
    uint8_t
416
      b = (uint8_t) shared_secret[i];
417
418
    k0^=b;
419
    k0*=0x100000001b3ULL;
420
    k1^=(uint64_t) b << ((i & 7)*8);
421
    k1=(k1 << 5) | (k1 >> (64-5));
422
  }
423
  (void) memcpy(key,&k0,8);
424
  (void) memcpy(key+8,&k1,8);
425
}
426
427
static inline uint64_t GenerateSessionKey(const char *shared_secret,
428
  const unsigned char *nonce,size_t length)
429
{
430
  uint8_t
431
    key[16];
432
433
  DeriveSIPKeyFromSecret(shared_secret,key);
434
  return(SIPHash24(key,nonce,length));
435
}
436
437
static int ConnectPixelCacheServer(const char *hostname,const int port,
438
  uint64_t *session_key,ExceptionInfo *exception)
439
{
440
#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
441
  char
442
    *message,
443
    service[MagickPathExtent],
444
    *shared_secret;
445
446
  int
447
    status;
448
449
  SOCKET_TYPE
450
    client_socket;
451
452
  ssize_t
453
    count;
454
455
  struct addrinfo
456
    hints,
457
    *result;
458
459
  unsigned char
460
    nonce[DPCSessionKeyLength];
461
462
  /*
463
    Connect to distributed pixel cache server and get session key.
464
  */
465
  *session_key=0;
466
#if defined(MAGICKCORE_HAVE_WINSOCK2)
467
  InitializeWinsock2(MagickTrue);
468
#endif
469
  (void) memset(&hints,0,sizeof(hints));
470
  hints.ai_family=AF_INET;
471
  hints.ai_socktype=SOCK_STREAM;
472
  hints.ai_flags=AI_PASSIVE;
473
  (void) FormatLocaleString(service,MagickPathExtent,"%d",port);
474
  status=getaddrinfo(hostname,service,&hints,&result);
475
  if (status != 0)
476
    {
477
      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
478
        "DistributedPixelCache","'%s': %s",hostname,gai_strerror(status));
479
      return(-1);
480
    }
481
  client_socket=(SOCKET_TYPE) socket(result->ai_family,result->ai_socktype,
482
    result->ai_protocol);
483
  if (client_socket == -1)
484
    {
485
      freeaddrinfo(result);
486
      message=GetExceptionMessage(errno);
487
      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
488
        "DistributedPixelCache","'%s': %s",hostname,message);
489
      message=DestroyString(message);
490
      return(-1);
491
    }
492
  status=connect(client_socket,result->ai_addr,(socklen_t) result->ai_addrlen);
493
  freeaddrinfo(result);
494
  if (status == -1)
495
    {
496
      CLOSE_SOCKET(client_socket);
497
      message=GetExceptionMessage(errno);
498
      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
499
        "DistributedPixelCache","'%s': %s",hostname,message);
500
      message=DestroyString(message);
501
      return(-1);
502
    }
503
  /*
504
    Receive server nonce.
505
  */
506
  count=recv(client_socket,(char *) nonce,sizeof(nonce),0);
507
  if (count != (ssize_t) sizeof(nonce))
508
    {
509
      CLOSE_SOCKET(client_socket);
510
      message=GetExceptionMessage(errno);
511
      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
512
        "DistributedPixelCache","'%s': %s",hostname,message);
513
      message=DestroyString(message);
514
      return(-1);
515
    }
516
  /*
517
    Compute keyed hash(shared_secret,nonce).
518
  */
519
  shared_secret=GetPolicyValue("cache:shared-secret");
520
  if (shared_secret == (const char *) NULL)
521
    {
522
      CLOSE_SOCKET(client_socket);
523
      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
524
        "DistributedPixelCache","'%s': shared secret required",hostname);
525
      return(-1);
526
    }
527
  *session_key=GenerateSessionKey(shared_secret,nonce,sizeof(nonce));
528
  shared_secret=DestroyString(shared_secret);
529
  /*
530
    Send keyed hash back to client.
531
  */
532
  count=send(client_socket,(char *) session_key,sizeof(*session_key),
533
    MSG_NOSIGNAL);
534
  if (count != (ssize_t) sizeof(*session_key))
535
    {
536
      CLOSE_SOCKET(client_socket);
537
      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
538
        "DistributedPixelCache","'%s': authentication failed",hostname);
539
      return(-1);
540
    }
541
  return((int) client_socket);
542
#else
543
  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
544
    "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
545
  return(-1);
546
#endif
547
}
548
#endif
549
550
static char *GetHostname(int *port,ExceptionInfo *exception)
551
0
{
552
0
  char
553
0
    *host,
554
0
    *hosts,
555
0
    **hostlist;
556
557
0
  int
558
0
    argc;
559
560
0
  ssize_t
561
0
    i;
562
563
0
  static size_t
564
0
    id = 0;
565
566
  /*
567
    Parse host list (e.g. 192.168.100.1:6668,192.168.100.2:6668).
568
  */
569
0
  hosts=(char *) GetImageRegistry(StringRegistryType,"cache:hosts",exception);
570
0
  if (hosts == (char *) NULL)
571
0
    {
572
0
      *port=DPCPort;
573
0
      return(AcquireString(DPCHostname));
574
0
    }
575
0
  (void) SubstituteString(&hosts,","," ");
576
0
  hostlist=StringToArgv(hosts,&argc);
577
0
  hosts=DestroyString(hosts);
578
0
  if ((hostlist == (char **) NULL) || ((argc-1) == 0))
579
0
    {
580
0
      *port=DPCPort;
581
0
      return(AcquireString(DPCHostname));
582
0
    }
583
0
  {
584
0
    size_t host_count = (size_t) argc-1;
585
0
    size_t index = (id++ % host_count)+1;
586
0
    hosts=AcquireString(hostlist[index]);
587
0
  }
588
0
  for (i=0; i < (ssize_t) argc; i++)
589
0
    hostlist[i]=DestroyString(hostlist[i]);
590
0
  hostlist=(char **) RelinquishMagickMemory(hostlist);
591
0
  (void) SubstituteString(&hosts,":"," ");
592
0
  hostlist=StringToArgv(hosts,&argc);
593
0
  if (hostlist == (char **) NULL)
594
0
    {
595
0
      *port=DPCPort;
596
0
      return(AcquireString(DPCHostname));
597
0
    }
598
0
  host=AcquireString(hostlist[1]);
599
0
  if (hostlist[2] == (char *) NULL)
600
0
    *port=DPCPort;
601
0
  else
602
0
    *port=(int) StringToLong(hostlist[2]);
603
0
  for (i=0; i < (ssize_t) argc; i++)
604
0
    hostlist[i]=DestroyString(hostlist[i]);
605
0
  hostlist=(char **) RelinquishMagickMemory(hostlist);
606
0
  return(host);
607
0
}
608
609
MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
610
  ExceptionInfo *exception)
611
0
{
612
0
  char
613
0
    *hostname;
614
615
0
  DistributeCacheInfo
616
0
    *server_info;
617
618
0
  uint64_t
619
0
    session_key;
620
621
  /*
622
    Connect to the distributed pixel cache server.
623
  */
624
0
  server_info=(DistributeCacheInfo *) AcquireCriticalMemory(
625
0
    sizeof(*server_info));
626
0
  (void) memset(server_info,0,sizeof(*server_info));
627
0
  server_info->signature=MagickCoreSignature;
628
0
  server_info->port=0;
629
0
  hostname=GetHostname(&server_info->port,exception);
630
0
  session_key=0;
631
0
  server_info->file=ConnectPixelCacheServer(hostname,server_info->port,
632
0
    &session_key,exception);
633
0
  if (server_info->file == -1)
634
0
    server_info=DestroyDistributeCacheInfo(server_info);
635
0
  else
636
0
    {
637
0
      server_info->session_key=session_key;
638
0
      (void) CopyMagickString(server_info->hostname,hostname,MagickPathExtent);
639
0
      server_info->debug=(GetLogEventMask() & CacheEvent) != 0 ? MagickTrue :
640
0
        MagickFalse;
641
0
    }
642
0
  hostname=DestroyString(hostname);
643
0
  return(server_info);
644
0
}
645

646
/*
647
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648
%                                                                             %
649
%                                                                             %
650
%                                                                             %
651
+   D e s t r o y D i s t r i b u t e C a c h e I n f o                       %
652
%                                                                             %
653
%                                                                             %
654
%                                                                             %
655
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
656
%
657
%  DestroyDistributeCacheInfo() deallocates memory associated with an
658
%  DistributeCacheInfo structure.
659
%
660
%  The format of the DestroyDistributeCacheInfo method is:
661
%
662
%      DistributeCacheInfo *DestroyDistributeCacheInfo(
663
%        DistributeCacheInfo *server_info)
664
%
665
%  A description of each parameter follows:
666
%
667
%    o server_info: the distributed cache info.
668
%
669
*/
670
MagickPrivate DistributeCacheInfo *DestroyDistributeCacheInfo(
671
  DistributeCacheInfo *server_info)
672
0
{
673
0
  assert(server_info != (DistributeCacheInfo *) NULL);
674
0
  assert(server_info->signature == MagickCoreSignature);
675
#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
676
  if (server_info->file >= 0)
677
    CLOSE_SOCKET(server_info->file);
678
#endif
679
0
  server_info->signature=(~MagickCoreSignature);
680
0
  server_info=(DistributeCacheInfo *) RelinquishMagickMemory(server_info);
681
0
  return(server_info);
682
0
}
683

684
/*
685
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
686
%                                                                             %
687
%                                                                             %
688
%                                                                             %
689
+   D i s t r i b u t e P i x e l C a c h e S e r v e r                       %
690
%                                                                             %
691
%                                                                             %
692
%                                                                             %
693
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
694
%
695
%  DistributePixelCacheServer() waits on the specified port for commands to
696
%  create, read, update, or destroy a pixel cache.
697
%
698
%  The format of the DistributePixelCacheServer() method is:
699
%
700
%      void DistributePixelCacheServer(const int port)
701
%
702
%  A description of each parameter follows:
703
%
704
%    o port: connect the distributed pixel cache at this port.
705
%
706
%    o exception: return any errors or warnings in this structure.
707
%
708
*/
709
710
#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
711
static inline MagickOffsetType dpc_send(SOCKET_TYPE magick_unused(file),
712
  const MagickSizeType magick_unused(length),
713
  const void *magick_restrict magick_unused(message))
714
0
{
715
0
  magick_unreferenced(file);
716
0
  magick_unreferenced(length);
717
0
  magick_unreferenced(message);
718
0
  return(-1);
719
0
}
720
#else
721
static inline MagickOffsetType dpc_send(SOCKET_TYPE file,
722
  const MagickSizeType length,const void *magick_restrict message)
723
{
724
  MagickOffsetType
725
    i;
726
727
  ssize_t
728
    count;
729
730
  /*
731
    Ensure a complete message is sent.
732
  */
733
  count=0;
734
  for (i=0; i < (MagickOffsetType) length; i+=count)
735
  {
736
    count=(ssize_t) send(file,(const char *) message+i,(LENGTH_TYPE) MagickMin(
737
      length-(MagickSizeType) i,(MagickSizeType) MagickMaxBufferExtent),
738
      MSG_NOSIGNAL);
739
    if (count <= 0)
740
      {
741
        count=0;
742
        if (errno != EINTR)
743
          break;
744
      }
745
  }
746
  return(i);
747
}
748
#endif
749
750
#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
751
MagickExport void DistributePixelCacheServer(const int magick_unused(port),
752
  ExceptionInfo *magick_unused(exception))
753
0
{
754
0
  magick_unreferenced(port);
755
0
  magick_unreferenced(exception);
756
0
  ThrowFatalException(MissingDelegateError,"DelegateLibrarySupportNotBuiltIn");
757
0
}
758
#else
759
static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *registry,
760
  const uint64_t session_key)
761
{
762
  MagickAddressType
763
    key = (MagickAddressType) session_key;
764
765
  /*
766
    Destroy distributed pixel cache.
767
  */
768
  return(DeleteNodeFromSplayTree(registry,(const void *) key));
769
}
770
771
static MagickBooleanType OpenDistributeCache(SplayTreeInfo *registry,
772
  SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
773
{
774
  Image
775
    *image;
776
777
  MagickAddressType
778
    key = (MagickAddressType) session_key;
779
780
  MagickBooleanType
781
    status;
782
783
  MagickOffsetType
784
    count;
785
786
  MagickSizeType
787
    length;
788
789
  unsigned char
790
    message[MagickPathExtent],
791
    *p;
792
793
  /*
794
    Open distributed pixel cache.
795
  */
796
  image=AcquireImage((ImageInfo *) NULL,exception);
797
  if (image == (Image *) NULL)
798
    return(MagickFalse);
799
  length=sizeof(image->storage_class)+sizeof(image->colorspace)+
800
    sizeof(image->alpha_trait)+sizeof(image->channels)+sizeof(image->columns)+
801
    sizeof(image->rows)+sizeof(image->number_channels)+MaxPixelChannels*
802
    sizeof(*image->channel_map)+sizeof(image->metacontent_extent);
803
  count=dpc_receive(file,length,message);
804
  if (count != (MagickOffsetType) length)
805
    {
806
      image=DestroyImage(image);
807
      return(MagickFalse);
808
    }
809
  /*
810
    Deserialize the image attributes.
811
  */
812
  p=message;
813
  (void) memcpy(&image->storage_class,p,sizeof(image->storage_class));
814
  p+=(ptrdiff_t) sizeof(image->storage_class);
815
  (void) memcpy(&image->colorspace,p,sizeof(image->colorspace));
816
  p+=(ptrdiff_t) sizeof(image->colorspace);
817
  (void) memcpy(&image->alpha_trait,p,sizeof(image->alpha_trait));
818
  p+=(ptrdiff_t) sizeof(image->alpha_trait);
819
  (void) memcpy(&image->channels,p,sizeof(image->channels));
820
  p+=(ptrdiff_t) sizeof(image->channels);
821
  (void) memcpy(&image->columns,p,sizeof(image->columns));
822
  p+=(ptrdiff_t) sizeof(image->columns);
823
  (void) memcpy(&image->rows,p,sizeof(image->rows));
824
  p+=(ptrdiff_t) sizeof(image->rows);
825
  (void) memcpy(&image->number_channels,p,sizeof(image->number_channels));
826
  p+=(ptrdiff_t) sizeof(image->number_channels);
827
  (void) memcpy(image->channel_map,p,MaxPixelChannels*
828
    sizeof(*image->channel_map));
829
  p+=(ptrdiff_t) MaxPixelChannels*sizeof(*image->channel_map);
830
  (void) memcpy(&image->metacontent_extent,p,sizeof(image->metacontent_extent));
831
  p+=(ptrdiff_t) sizeof(image->metacontent_extent);
832
  if (SyncImagePixelCache(image,exception) == MagickFalse)
833
    {
834
      image=DestroyImage(image);
835
      return(MagickFalse);
836
    }
837
  status=AddValueToSplayTree(registry,(const void *) key,image);
838
  if (status == MagickFalse)
839
    {
840
      image=DestroyImage(image);
841
      return(MagickFalse);
842
    }
843
  return(status);
844
}
845
846
static inline MagickBooleanType ValidateDistributedPixelCache(
847
  const RectangleInfo *region,const size_t per_pixel,
848
  const MagickSizeType length)
849
{
850
  size_t
851
    extent = 0,
852
    pixels = 0;
853
854
  if (HeapOverflowSanityCheckGetSize(region->width,region->height,&pixels) != MagickFalse)
855
    return(MagickFalse);
856
  if (HeapOverflowSanityCheckGetSize(pixels,per_pixel,&extent) != MagickFalse)
857
    return(MagickFalse);
858
  if (length > (MagickSizeType) extent)
859
    return(MagickFalse);
860
  return(MagickTrue);
861
}
862
863
static MagickBooleanType ReadDistributeCacheMetacontent(SplayTreeInfo *registry,
864
  SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
865
{
866
  const Quantum
867
    *p;
868
869
  const unsigned char
870
    *metacontent;
871
872
  Image
873
    *image;
874
875
  MagickAddressType
876
    key = (MagickAddressType) session_key;
877
878
  MagickOffsetType
879
    count;
880
881
  MagickSizeType
882
    length;
883
884
  RectangleInfo
885
    region;
886
887
  size_t
888
    per_pixel;
889
890
  unsigned char
891
    message[MagickPathExtent],
892
    *q;
893
894
  /*
895
    Read distributed pixel cache metacontent.
896
  */
897
  image=(Image *) GetValueFromSplayTree(registry,(const void *) key);
898
  if (image == (Image *) NULL)
899
    return(MagickFalse);
900
  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
901
    sizeof(region.y)+sizeof(length);
902
  count=dpc_receive(file,length,message);
903
  if (count != (MagickOffsetType) length)
904
    return(MagickFalse);
905
  q=message;
906
  (void) memcpy(&region.width,q,sizeof(region.width));
907
  q+=(ptrdiff_t) sizeof(region.width);
908
  (void) memcpy(&region.height,q,sizeof(region.height));
909
  q+=(ptrdiff_t) sizeof(region.height);
910
  (void) memcpy(&region.x,q,sizeof(region.x));
911
  q+=(ptrdiff_t) sizeof(region.x);
912
  (void) memcpy(&region.y,q,sizeof(region.y));
913
  q+=(ptrdiff_t) sizeof(region.y);
914
  (void) memcpy(&length,q,sizeof(length));
915
  q+=(ptrdiff_t) sizeof(length);
916
  per_pixel=image->number_meta_channels*sizeof(Quantum);
917
  if (ValidateDistributedPixelCache(&region,per_pixel,length) == MagickFalse)
918
    return(MagickFalse);
919
  p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
920
    exception);
921
  if (p == (const Quantum *) NULL)
922
    return(MagickFalse);
923
  metacontent=(const unsigned char *) GetVirtualMetacontent(image);
924
  count=dpc_send(file,length,metacontent);
925
  if (count != (MagickOffsetType) length)
926
    return(MagickFalse);
927
  return(MagickTrue);
928
}
929
930
static MagickBooleanType ReadDistributeCachePixels(SplayTreeInfo *registry,
931
  SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
932
{
933
  const Quantum
934
    *p;
935
936
  Image
937
    *image;
938
939
  MagickAddressType
940
    key = (MagickAddressType) session_key;
941
942
  MagickOffsetType
943
    count;
944
945
  MagickSizeType
946
    length;
947
948
  RectangleInfo
949
    region;
950
951
  size_t
952
    per_pixel;
953
954
  unsigned char
955
    message[MagickPathExtent],
956
    *q;
957
958
  /*
959
    Read distributed pixel cache pixels.
960
  */
961
  image=(Image *) GetValueFromSplayTree(registry,(const void *) key);
962
  if (image == (Image *) NULL)
963
    return(MagickFalse);
964
  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
965
    sizeof(region.y)+sizeof(length);
966
  count=dpc_receive(file,length,message);
967
  if (count != (MagickOffsetType) length)
968
    return(MagickFalse);
969
  q=message;
970
  (void) memcpy(&region.width,q,sizeof(region.width));
971
  q+=(ptrdiff_t) sizeof(region.width);
972
  (void) memcpy(&region.height,q,sizeof(region.height));
973
  q+=(ptrdiff_t) sizeof(region.height);
974
  (void) memcpy(&region.x,q,sizeof(region.x));
975
  q+=(ptrdiff_t) sizeof(region.x);
976
  (void) memcpy(&region.y,q,sizeof(region.y));
977
  q+=(ptrdiff_t) sizeof(region.y);
978
  (void) memcpy(&length,q,sizeof(length));
979
  per_pixel=image->number_channels*sizeof(Quantum);
980
  if (ValidateDistributedPixelCache(&region,per_pixel,length) == MagickFalse)
981
    return(MagickFalse);
982
  q+=(ptrdiff_t) sizeof(length);
983
  p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
984
    exception);
985
  if (p == (const Quantum *) NULL)
986
    return(MagickFalse);
987
  count=dpc_send(file,length,p);
988
  if (count != (MagickOffsetType) length)
989
    return(MagickFalse);
990
  return(MagickTrue);
991
}
992
993
static void *RelinquishImageRegistry(void *image)
994
{
995
  return((void *) DestroyImageList((Image *) image));
996
}
997
998
static MagickBooleanType WriteDistributeCacheMetacontent(
999
  SplayTreeInfo *registry,SOCKET_TYPE file,const uint64_t session_key,
1000
  ExceptionInfo *exception)
1001
{
1002
  Image
1003
    *image;
1004
1005
  MagickAddressType
1006
    key = (MagickAddressType) session_key;
1007
1008
  MagickOffsetType
1009
    count;
1010
1011
  MagickSizeType
1012
    length;
1013
1014
  Quantum
1015
    *q;
1016
1017
  RectangleInfo
1018
    region;
1019
1020
  size_t
1021
    per_pixel;
1022
1023
  unsigned char
1024
    message[MagickPathExtent],
1025
    *metacontent,
1026
    *p;
1027
1028
  /*
1029
    Write distributed pixel cache metacontent.
1030
  */
1031
  image=(Image *) GetValueFromSplayTree(registry,(const void *) key);
1032
  if (image == (Image *) NULL)
1033
    return(MagickFalse);
1034
  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
1035
    sizeof(region.y)+sizeof(length);
1036
  count=dpc_receive(file,length,message);
1037
  if (count != (MagickOffsetType) length)
1038
    return(MagickFalse);
1039
  p=message;
1040
  (void) memcpy(&region.width,p,sizeof(region.width));
1041
  p+=(ptrdiff_t) sizeof(region.width);
1042
  (void) memcpy(&region.height,p,sizeof(region.height));
1043
  p+=(ptrdiff_t) sizeof(region.height);
1044
  (void) memcpy(&region.x,p,sizeof(region.x));
1045
  p+=(ptrdiff_t) sizeof(region.x);
1046
  (void) memcpy(&region.y,p,sizeof(region.y));
1047
  p+=(ptrdiff_t) sizeof(region.y);
1048
  (void) memcpy(&length,p,sizeof(length));
1049
  per_pixel=image->number_meta_channels*sizeof(Quantum);
1050
  if (ValidateDistributedPixelCache(&region,per_pixel,length) == MagickFalse)
1051
    return(MagickFalse);
1052
  p+=(ptrdiff_t) sizeof(length);
1053
  q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
1054
    exception);
1055
  if (q == (Quantum *) NULL)
1056
    return(MagickFalse);
1057
  metacontent=(unsigned char *) GetAuthenticMetacontent(image);
1058
  count=dpc_receive(file,length,metacontent);
1059
  if (count != (MagickOffsetType) length)
1060
    return(MagickFalse);
1061
  return(SyncAuthenticPixels(image,exception));
1062
}
1063
1064
static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
1065
  SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
1066
{
1067
  Image
1068
    *image;
1069
1070
  MagickAddressType
1071
    key = (MagickAddressType) session_key;
1072
1073
  MagickOffsetType
1074
    count;
1075
1076
  MagickSizeType
1077
    length;
1078
1079
  Quantum
1080
    *q;
1081
1082
  RectangleInfo
1083
    region;
1084
1085
  size_t
1086
    per_pixel;
1087
1088
  unsigned char
1089
    message[MagickPathExtent],
1090
    *p;
1091
1092
  /*
1093
    Write distributed pixel cache pixels.
1094
  */
1095
  image=(Image *) GetValueFromSplayTree(registry,(const void *) key);
1096
  if (image == (Image *) NULL)
1097
    return(MagickFalse);
1098
  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
1099
    sizeof(region.y)+sizeof(length);
1100
  count=dpc_receive(file,length,message);
1101
  if (count != (MagickOffsetType) length)
1102
    return(MagickFalse);
1103
  p=message;
1104
  (void) memcpy(&region.width,p,sizeof(region.width));
1105
  p+=(ptrdiff_t) sizeof(region.width);
1106
  (void) memcpy(&region.height,p,sizeof(region.height));
1107
  p+=(ptrdiff_t) sizeof(region.height);
1108
  (void) memcpy(&region.x,p,sizeof(region.x));
1109
  p+=(ptrdiff_t) sizeof(region.x);
1110
  (void) memcpy(&region.y,p,sizeof(region.y));
1111
  p+=(ptrdiff_t) sizeof(region.y);
1112
  (void) memcpy(&length,p,sizeof(length));
1113
  per_pixel=image->number_channels*sizeof(Quantum);
1114
  if (ValidateDistributedPixelCache(&region,per_pixel,length) == MagickFalse)
1115
    return(MagickFalse);
1116
  p+=(ptrdiff_t) sizeof(length);
1117
  q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
1118
    exception);
1119
  if (q == (Quantum *) NULL)
1120
    return(MagickFalse);
1121
  count=dpc_receive(file,length,(unsigned char *) q);
1122
  if (count != (MagickOffsetType) length)
1123
    return(MagickFalse);
1124
  return(SyncAuthenticPixels(image,exception));
1125
}
1126
1127
static void LockDPCSemaphore(void)
1128
{
1129
  if (dpc_semaphore == (SemaphoreInfo *) NULL)
1130
    ActivateSemaphoreInfo(&dpc_semaphore);
1131
  LockSemaphoreInfo(dpc_semaphore);
1132
}
1133
1134
static void AuthenticateDPCClient(void)
1135
{
1136
  LockDPCSemaphore();
1137
  if (dpc_unauthenticated_clients != 0)
1138
    dpc_unauthenticated_clients--;
1139
  UnlockSemaphoreInfo(dpc_semaphore);
1140
}
1141
1142
static void RelinquishDPCClient(const MagickBooleanType unauthenticated)
1143
{
1144
  LockDPCSemaphore();
1145
  if ((unauthenticated != MagickFalse) && (dpc_unauthenticated_clients != 0))
1146
    dpc_unauthenticated_clients--;
1147
  if (dpc_clients != 0)
1148
    dpc_clients--;
1149
  UnlockSemaphoreInfo(dpc_semaphore);
1150
}
1151
1152
static HANDLER_RETURN_TYPE DistributePixelCacheConnectionHandler(void *socket_arg)
1153
{
1154
  ExceptionInfo
1155
    *exception;
1156
1157
  MagickBooleanType
1158
    authenticated = MagickFalse,
1159
    status = MagickFalse;
1160
1161
  MagickOffsetType
1162
    count;
1163
1164
  RandomInfo
1165
    *random_info;
1166
1167
  SOCKET_TYPE
1168
    client_socket,
1169
    *client_socket_ptr = (SOCKET_TYPE *) socket_arg;
1170
1171
  SplayTreeInfo
1172
    *registry;
1173
1174
  StringInfo
1175
    *entropy;
1176
1177
  uint64_t
1178
    key,
1179
    session_key;
1180
1181
  unsigned char
1182
    command,
1183
    nonce[DPCSessionKeyLength];
1184
1185
  client_socket=(*client_socket_ptr);
1186
  client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(client_socket_ptr);
1187
  /*
1188
    Generate random nonce.
1189
  */
1190
  random_info=AcquireRandomInfo();
1191
  entropy=GetRandomKey(random_info,sizeof(nonce));
1192
  (void) memcpy(nonce,GetStringInfoDatum(entropy),sizeof(nonce));
1193
  entropy=DestroyStringInfo(entropy);
1194
  random_info=DestroyRandomInfo(random_info);
1195
  /*
1196
    Derive session key.
1197
  */
1198
  session_key=GenerateSessionKey(dpc_shared_secret,nonce,sizeof(nonce));
1199
  /*
1200
    Send nonce to client.
1201
  */
1202
  count=dpc_send(client_socket,sizeof(nonce),nonce);
1203
  if (count != (MagickOffsetType) sizeof(nonce))
1204
    {
1205
      CLOSE_SOCKET(client_socket);
1206
      RelinquishDPCClient(MagickTrue);
1207
      return(HANDLER_RETURN_VALUE);
1208
    }
1209
  /*
1210
    Receive client's keyed hash.
1211
  */
1212
  count=dpc_receive_deadline(client_socket,sizeof(key),(unsigned char *) &key,
1213
    (double) time((time_t *) NULL)+5.0);
1214
  if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
1215
    {
1216
      CLOSE_SOCKET(client_socket);
1217
      RelinquishDPCClient(MagickTrue);
1218
      return(HANDLER_RETURN_VALUE);
1219
    }
1220
  AuthenticateDPCClient();
1221
  authenticated=MagickTrue;
1222
  exception=AcquireExceptionInfo();
1223
  registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
1224
    (void *(*)(void *)) NULL,RelinquishImageRegistry);
1225
  /*
1226
    Command loop.
1227
  */
1228
  for (status=MagickFalse; ; )
1229
  {
1230
    /*
1231
      Each command must echo the authenticated session key.
1232
    */
1233
    count=dpc_receive_deadline(client_socket,1,(unsigned char *) &command,
1234
      (double) time((time_t *) NULL)+5.0);
1235
    if (count <= 0)
1236
      break;
1237
    count=dpc_receive_deadline(client_socket,sizeof(key),(unsigned char *) &key,
1238
      (double) time((time_t *) NULL)+5.0);
1239
    if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
1240
      break;
1241
    switch (command)
1242
    {
1243
      case 'o':
1244
      {
1245
        status=OpenDistributeCache(registry,client_socket,session_key,
1246
          exception);
1247
        dpc_send(client_socket,sizeof(status),&status);
1248
        break;
1249
      }
1250
      case 'r':
1251
      {
1252
        status=ReadDistributeCachePixels(registry,client_socket,session_key,
1253
          exception);
1254
        break;
1255
      }
1256
      case 'R':
1257
      {
1258
        status=ReadDistributeCacheMetacontent(registry,client_socket,
1259
          session_key,exception);
1260
        break;
1261
      }
1262
      case 'w':
1263
      {
1264
        status=WriteDistributeCachePixels(registry,client_socket,session_key,
1265
          exception);
1266
        break;
1267
      }
1268
      case 'W':
1269
      {
1270
        status=WriteDistributeCacheMetacontent(registry,client_socket,
1271
          session_key,exception);
1272
        break;
1273
      }
1274
      case 'd':
1275
      {
1276
        status=DestroyDistributeCache(registry,session_key);
1277
        break;
1278
      }
1279
      default:
1280
        break;
1281
    }
1282
    if ((status == MagickFalse) || (command == 'd'))
1283
      break;
1284
  }
1285
  count=dpc_send(client_socket,sizeof(status),&status);
1286
  CLOSE_SOCKET(client_socket);
1287
  exception=DestroyExceptionInfo(exception);
1288
  registry=DestroySplayTree(registry);
1289
  RelinquishDPCClient(authenticated == MagickFalse ? MagickTrue :
1290
    MagickFalse);
1291
  return(HANDLER_RETURN_VALUE);
1292
}
1293
1294
static size_t GetDPCPolicyLimit(const char *name,const size_t default_limit)
1295
{
1296
  char
1297
    policy_name[MagickPathExtent],
1298
    *policy_value;
1299
1300
  size_t
1301
    limit;
1302
1303
  limit=default_limit;
1304
  (void) FormatLocaleString(policy_name,MagickPathExtent,"cache:%s",name);
1305
  policy_value=GetPolicyValue(policy_name);
1306
  if (policy_value != (char *) NULL)
1307
    {
1308
      char
1309
        *q;
1310
1311
      unsigned long
1312
        policy_limit;
1313
1314
      errno=0;
1315
      policy_limit=strtoul(policy_value,&q,10);
1316
      if ((errno == 0) && (q != policy_value) && (policy_limit != 0))
1317
        limit=(size_t) policy_limit;
1318
      policy_value=DestroyString(policy_value);
1319
    }
1320
  return(limit);
1321
}
1322
1323
static size_t GetMaxDPCClients(void)
1324
{
1325
  return(GetDPCPolicyLimit("max-dpc-clients",DPCMaxClientWorkers));
1326
}
1327
1328
static size_t GetMaxDPCUnauthenticatedClients(void)
1329
{
1330
  size_t
1331
    client_workers,
1332
    unauthenticated_workers;
1333
1334
  client_workers=GetMaxDPCClients();
1335
  unauthenticated_workers=GetDPCPolicyLimit("max-dpc-unauthenticated-clients",
1336
    DPCMaxUnauthenticatedClientWorkers);
1337
  return(MagickMin(unauthenticated_workers,client_workers));
1338
}
1339
1340
static MagickBooleanType AcquireDPCClientWorker(void)
1341
{
1342
  MagickBooleanType
1343
    status;
1344
1345
  size_t
1346
    max_dpc_clients,
1347
    unauthenticated_worker_limit;
1348
1349
  status=MagickFalse;
1350
  max_dpc_clients=GetMaxDPCClients();
1351
  unauthenticated_worker_limit=GetMaxDPCUnauthenticatedClients();
1352
  LockDPCSemaphore();
1353
  if ((dpc_clients < max_dpc_clients) &&
1354
      (dpc_unauthenticated_clients < unauthenticated_worker_limit))
1355
    {
1356
      dpc_clients++;
1357
      dpc_unauthenticated_clients++;
1358
      status=MagickTrue;
1359
    }
1360
  UnlockSemaphoreInfo(dpc_semaphore);
1361
  return(status);
1362
}
1363
1364
MagickExport void DistributePixelCacheServer(const int port,
1365
  ExceptionInfo *exception)
1366
{
1367
  char
1368
    service[MagickPathExtent];
1369
1370
  int
1371
    status;
1372
1373
#if defined(MAGICKCORE_THREAD_SUPPORT)
1374
  pthread_attr_t
1375
    attributes;
1376
1377
  pthread_t
1378
    thread_id;
1379
#elif defined(_MSC_VER)
1380
  DWORD
1381
    threadID;
1382
#else
1383
  Not implemented!
1384
#endif
1385
1386
  SOCKET_TYPE
1387
    server_socket;
1388
1389
  struct addrinfo
1390
    *p;
1391
1392
  struct addrinfo
1393
    hint,
1394
    *result;
1395
1396
  struct sockaddr_in
1397
    address;
1398
1399
  /*
1400
    Launch distributed pixel cache server.
1401
  */
1402
  assert(exception != (ExceptionInfo *) NULL);
1403
  assert(exception->signature == MagickCoreSignature);
1404
  magick_unreferenced(exception);
1405
  dpc_shared_secret=GetPolicyValue("cache:shared-secret");
1406
  if (dpc_shared_secret == (char *) NULL)
1407
    ThrowFatalException(CacheFatalError,"no shared secret");
1408
#if defined(MAGICKCORE_HAVE_WINSOCK2)
1409
  InitializeWinsock2(MagickFalse);
1410
#endif
1411
  (void) memset(&hint,0,sizeof(hint));
1412
  hint.ai_family=AF_INET;
1413
  hint.ai_socktype=SOCK_STREAM;
1414
  hint.ai_flags=AI_PASSIVE;
1415
  FormatLocaleString(service,MagickPathExtent,"%d",port);
1416
  status=getaddrinfo(NULL,service,&hint,&result);
1417
  if (status != 0)
1418
    ThrowFatalException(CacheFatalError,"UnableToListen");
1419
  server_socket=(SOCKET_TYPE) 0;
1420
  for (p=result; p != NULL; p=p->ai_next)
1421
  {
1422
    int
1423
      one = 1;
1424
1425
    server_socket=(SOCKET_TYPE) socket(p->ai_family,p->ai_socktype,
1426
      p->ai_protocol);
1427
    if (server_socket == -1)
1428
      continue;
1429
    status=setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,(char *) &one,
1430
      (socklen_t) sizeof(one));
1431
    if (status == -1)
1432
      {
1433
        CLOSE_SOCKET(server_socket);
1434
        continue;
1435
      }
1436
    SetSocketReceiveTimeout(server_socket,5.0);
1437
    status=bind(server_socket,p->ai_addr,(socklen_t) p->ai_addrlen);
1438
    if (status == -1)
1439
      {
1440
        CLOSE_SOCKET(server_socket);
1441
        continue;
1442
      }
1443
    break;
1444
  }
1445
  if (p == (struct addrinfo *) NULL)
1446
    ThrowFatalException(CacheFatalError,"UnableToBind");
1447
  freeaddrinfo(result);
1448
  status=listen(server_socket,DPCPendingConnections);
1449
  if (status != 0)
1450
    ThrowFatalException(CacheFatalError,"UnableToListen");
1451
#if defined(MAGICKCORE_THREAD_SUPPORT)
1452
  pthread_attr_init(&attributes);
1453
  pthread_attr_setdetachstate(&attributes,PTHREAD_CREATE_DETACHED);
1454
#endif
1455
  for ( ; ; )
1456
  {
1457
    SOCKET_TYPE
1458
      *client_socket_ptr;
1459
1460
    socklen_t
1461
      length = (socklen_t) sizeof(address);
1462
1463
    client_socket_ptr=(SOCKET_TYPE *) AcquireMagickMemory(sizeof(SOCKET_TYPE));
1464
    if (client_socket_ptr == NULL)
1465
      continue;  /* skip connection */
1466
    *client_socket_ptr=(SOCKET_TYPE) accept(server_socket,(struct sockaddr *)
1467
      &address,&length);
1468
    if (*client_socket_ptr == -1)
1469
      {
1470
        client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(
1471
          client_socket_ptr);
1472
        continue;
1473
      }
1474
    if (AcquireDPCClientWorker() == MagickFalse)
1475
      {
1476
        CLOSE_SOCKET(*client_socket_ptr);
1477
        client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(
1478
          client_socket_ptr);
1479
        continue;
1480
      }
1481
    SetSocketReceiveTimeout(*client_socket_ptr,5.0);
1482
    if (status == -1)
1483
      {
1484
        CLOSE_SOCKET(*client_socket_ptr);
1485
        RelinquishDPCClient(MagickTrue);
1486
        client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(
1487
          client_socket_ptr);
1488
        continue;
1489
      }
1490
#if defined(MAGICKCORE_THREAD_SUPPORT)
1491
    status=pthread_create(&thread_id,&attributes,DistributePixelCacheConnectionHandler,
1492
      (void *) client_socket_ptr);
1493
    if (status != 0)
1494
      {
1495
        CLOSE_SOCKET(*client_socket_ptr);
1496
        RelinquishDPCClient(MagickTrue);
1497
        RelinquishMagickMemory(client_socket_ptr);
1498
        continue;
1499
      }
1500
#elif defined(_MSC_VER)
1501
    if (CreateThread(0,0,DistributePixelCacheConnectionHandler,(void*) client_socket_ptr,
1502
          0,&threadID) == (HANDLE) NULL)
1503
      {
1504
        CLOSE_SOCKET(*client_socket_ptr);
1505
        RelinquishDPCClient(MagickTrue);
1506
        RelinquishMagickMemory(client_socket_ptr);
1507
        continue;
1508
      }
1509
#else
1510
    Not implemented!
1511
#endif
1512
  }
1513
}
1514
#endif
1515

1516
/*
1517
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1518
%                                                                             %
1519
%                                                                             %
1520
%                                                                             %
1521
+  D i s t r i b u t e C a c h e T e r m i n u s                              %
1522
%                                                                             %
1523
%                                                                             %
1524
%                                                                             %
1525
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1526
%
1527
%  DistributeCacheTerminus() destroys the Distributed Cache.
1528
%
1529
*/
1530
MagickPrivate void DistributeCacheTerminus(void)
1531
0
{
1532
#ifdef MAGICKCORE_HAVE_WINSOCK2
1533
  if (winsock_semaphore == (SemaphoreInfo *) NULL)
1534
    ActivateSemaphoreInfo(&winsock_semaphore);
1535
  LockSemaphoreInfo(winsock_semaphore);
1536
  if (wsaData != (WSADATA *) NULL)
1537
    {
1538
      WSACleanup();
1539
      wsaData=(WSADATA *) RelinquishMagickMemory((void *) wsaData);
1540
    }
1541
  UnlockSemaphoreInfo(winsock_semaphore);
1542
  RelinquishSemaphoreInfo(&winsock_semaphore);
1543
#endif
1544
#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
1545
  if (dpc_shared_secret != (char *) NULL)
1546
    dpc_shared_secret=DestroyString(dpc_shared_secret);
1547
  if (dpc_semaphore != (SemaphoreInfo *) NULL)
1548
    RelinquishSemaphoreInfo(&dpc_semaphore);
1549
#endif
1550
0
}
1551
1552
/*
1553
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1554
%                                                                             %
1555
%                                                                             %
1556
%                                                                             %
1557
+   G e t D i s t r i b u t e C a c h e F i l e                               %
1558
%                                                                             %
1559
%                                                                             %
1560
%                                                                             %
1561
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1562
%
1563
%  GetDistributeCacheFile() returns the file associated with this
1564
%  DistributeCacheInfo structure.
1565
%
1566
%  The format of the GetDistributeCacheFile method is:
1567
%
1568
%      int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
1569
%
1570
%  A description of each parameter follows:
1571
%
1572
%    o server_info: the distributed cache info.
1573
%
1574
*/
1575
MagickPrivate int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
1576
0
{
1577
0
  assert(server_info != (DistributeCacheInfo *) NULL);
1578
0
  assert(server_info->signature == MagickCoreSignature);
1579
0
  return(server_info->file);
1580
0
}
1581

1582
/*
1583
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584
%                                                                             %
1585
%                                                                             %
1586
%                                                                             %
1587
+   G e t D i s t r i b u t e C a c h e H o s t n a m e                       %
1588
%                                                                             %
1589
%                                                                             %
1590
%                                                                             %
1591
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1592
%
1593
%  GetDistributeCacheHostname() returns the hostname associated with this
1594
%  DistributeCacheInfo structure.
1595
%
1596
%  The format of the GetDistributeCacheHostname method is:
1597
%
1598
%      const char *GetDistributeCacheHostname(
1599
%        const DistributeCacheInfo *server_info)
1600
%
1601
%  A description of each parameter follows:
1602
%
1603
%    o server_info: the distributed cache info.
1604
%
1605
*/
1606
MagickPrivate const char *GetDistributeCacheHostname(
1607
  const DistributeCacheInfo *server_info)
1608
0
{
1609
0
  assert(server_info != (DistributeCacheInfo *) NULL);
1610
0
  assert(server_info->signature == MagickCoreSignature);
1611
0
  return(server_info->hostname);
1612
0
}
1613

1614
/*
1615
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616
%                                                                             %
1617
%                                                                             %
1618
%                                                                             %
1619
+   G e t D i s t r i b u t e C a c h e P o r t                               %
1620
%                                                                             %
1621
%                                                                             %
1622
%                                                                             %
1623
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624
%
1625
%  GetDistributeCachePort() returns the port associated with this
1626
%  DistributeCacheInfo structure.
1627
%
1628
%  The format of the GetDistributeCachePort method is:
1629
%
1630
%      int GetDistributeCachePort(const DistributeCacheInfo *server_info)
1631
%
1632
%  A description of each parameter follows:
1633
%
1634
%    o server_info: the distributed cache info.
1635
%
1636
*/
1637
MagickPrivate int GetDistributeCachePort(const DistributeCacheInfo *server_info)
1638
0
{
1639
0
  assert(server_info != (DistributeCacheInfo *) NULL);
1640
0
  assert(server_info->signature == MagickCoreSignature);
1641
0
  return(server_info->port);
1642
0
}
1643

1644
/*
1645
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646
%                                                                             %
1647
%                                                                             %
1648
%                                                                             %
1649
+   O p e n D i s t r i b u t e P i x e l C a c h e                           %
1650
%                                                                             %
1651
%                                                                             %
1652
%                                                                             %
1653
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1654
%
1655
%  OpenDistributePixelCache() opens a pixel cache on a remote server.
1656
%
1657
%  The format of the OpenDistributePixelCache method is:
1658
%
1659
%      MagickBooleanType *OpenDistributePixelCache(
1660
%        DistributeCacheInfo *server_info,Image *image)
1661
%
1662
%  A description of each parameter follows:
1663
%
1664
%    o server_info: the distributed cache info.
1665
%
1666
%    o image: the image.
1667
%
1668
*/
1669
MagickPrivate MagickBooleanType OpenDistributePixelCache(
1670
  DistributeCacheInfo *server_info,Image *image)
1671
0
{
1672
0
  MagickBooleanType
1673
0
    status;
1674
1675
0
  MagickOffsetType
1676
0
    count;
1677
1678
0
  unsigned char
1679
0
    message[MagickPathExtent],
1680
0
    *p;
1681
1682
  /*
1683
    Open distributed pixel cache.
1684
  */
1685
0
  assert(server_info != (DistributeCacheInfo *) NULL);
1686
0
  assert(server_info->signature == MagickCoreSignature);
1687
0
  assert(image != (Image *) NULL);
1688
0
  assert(image->signature == MagickCoreSignature);
1689
0
  p=message;
1690
0
  *p++='o';  /* open */
1691
  /*
1692
    Serialize image attributes (see ValidatePixelCacheMorphology()).
1693
  */
1694
0
  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1695
0
  p+=(ptrdiff_t) sizeof(server_info->session_key);
1696
0
  (void) memcpy(p,&image->storage_class,sizeof(image->storage_class));
1697
0
  p+=(ptrdiff_t) sizeof(image->storage_class);
1698
0
  (void) memcpy(p,&image->colorspace,sizeof(image->colorspace));
1699
0
  p+=(ptrdiff_t) sizeof(image->colorspace);
1700
0
  (void) memcpy(p,&image->alpha_trait,sizeof(image->alpha_trait));
1701
0
  p+=(ptrdiff_t) sizeof(image->alpha_trait);
1702
0
  (void) memcpy(p,&image->channels,sizeof(image->channels));
1703
0
  p+=(ptrdiff_t) sizeof(image->channels);
1704
0
  (void) memcpy(p,&image->columns,sizeof(image->columns));
1705
0
  p+=(ptrdiff_t) sizeof(image->columns);
1706
0
  (void) memcpy(p,&image->rows,sizeof(image->rows));
1707
0
  p+=(ptrdiff_t) sizeof(image->rows);
1708
0
  (void) memcpy(p,&image->number_channels,sizeof(image->number_channels));
1709
0
  p+=(ptrdiff_t) sizeof(image->number_channels);
1710
0
  (void) memcpy(p,image->channel_map,MaxPixelChannels*
1711
0
    sizeof(*image->channel_map));
1712
0
  p+=(ptrdiff_t) MaxPixelChannels*sizeof(*image->channel_map);
1713
0
  (void) memcpy(p,&image->metacontent_extent,sizeof(image->metacontent_extent));
1714
0
  p+=(ptrdiff_t) sizeof(image->metacontent_extent);
1715
0
  count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
1716
0
  if (count != (MagickOffsetType) (p-message))
1717
0
    return(MagickFalse);
1718
0
  status=MagickFalse;
1719
0
  count=dpc_receive(server_info->file,sizeof(status),(unsigned char *) &status);
1720
0
  if (count != (MagickOffsetType) sizeof(status))
1721
0
    return(MagickFalse);
1722
0
  return(status);
1723
0
}
1724

1725
/*
1726
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727
%                                                                             %
1728
%                                                                             %
1729
%                                                                             %
1730
+   R e a d D i s t r i b u t e P i x e l C a c h e M e t a c o n t e n t     %
1731
%                                                                             %
1732
%                                                                             %
1733
%                                                                             %
1734
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1735
%
1736
%  ReadDistributePixelCacheMetacontents() reads metacontent from the specified
1737
%  region of the distributed pixel cache.
1738
%
1739
%  The format of the ReadDistributePixelCacheMetacontents method is:
1740
%
1741
%      MagickOffsetType ReadDistributePixelCacheMetacontents(
1742
%        DistributeCacheInfo *server_info,const RectangleInfo *region,
1743
%        const MagickSizeType length,unsigned char *metacontent)
1744
%
1745
%  A description of each parameter follows:
1746
%
1747
%    o server_info: the distributed cache info.
1748
%
1749
%    o image: the image.
1750
%
1751
%    o region: read the metacontent from this region of the image.
1752
%
1753
%    o length: the length in bytes of the metacontent.
1754
%
1755
%    o metacontent: read these metacontent from the pixel cache.
1756
%
1757
*/
1758
MagickPrivate MagickOffsetType ReadDistributePixelCacheMetacontent(
1759
  DistributeCacheInfo *server_info,const RectangleInfo *region,
1760
  const MagickSizeType length,unsigned char *metacontent)
1761
0
{
1762
0
  MagickOffsetType
1763
0
    count;
1764
1765
0
  unsigned char
1766
0
    message[MagickPathExtent],
1767
0
    *p;
1768
1769
  /*
1770
    Read distributed pixel cache metacontent.
1771
  */
1772
0
  assert(server_info != (DistributeCacheInfo *) NULL);
1773
0
  assert(server_info->signature == MagickCoreSignature);
1774
0
  assert(region != (RectangleInfo *) NULL);
1775
0
  assert(metacontent != (unsigned char *) NULL);
1776
0
  if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
1777
0
    return(-1);
1778
0
  p=message;
1779
0
  *p++='R';
1780
0
  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1781
0
  p+=(ptrdiff_t) sizeof(server_info->session_key);
1782
0
  (void) memcpy(p,&region->width,sizeof(region->width));
1783
0
  p+=(ptrdiff_t) sizeof(region->width);
1784
0
  (void) memcpy(p,&region->height,sizeof(region->height));
1785
0
  p+=(ptrdiff_t) sizeof(region->height);
1786
0
  (void) memcpy(p,&region->x,sizeof(region->x));
1787
0
  p+=(ptrdiff_t) sizeof(region->x);
1788
0
  (void) memcpy(p,&region->y,sizeof(region->y));
1789
0
  p+=(ptrdiff_t) sizeof(region->y);
1790
0
  (void) memcpy(p,&length,sizeof(length));
1791
0
  p+=(ptrdiff_t) sizeof(length);
1792
0
  count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
1793
0
  if (count != (MagickOffsetType) (p-message))
1794
0
    return(-1);
1795
0
  return(dpc_receive(server_info->file,length,metacontent));
1796
0
}
1797

1798
/*
1799
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1800
%                                                                             %
1801
%                                                                             %
1802
%                                                                             %
1803
+   R e a d D i s t r i b u t e P i x e l C a c h e P i x e l s               %
1804
%                                                                             %
1805
%                                                                             %
1806
%                                                                             %
1807
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1808
%
1809
%  ReadDistributePixelCachePixels() reads pixels from the specified region of
1810
%  the distributed pixel cache.
1811
%
1812
%  The format of the ReadDistributePixelCachePixels method is:
1813
%
1814
%      MagickOffsetType ReadDistributePixelCachePixels(
1815
%        DistributeCacheInfo *server_info,const RectangleInfo *region,
1816
%        const MagickSizeType length,unsigned char *magick_restrict pixels)
1817
%
1818
%  A description of each parameter follows:
1819
%
1820
%    o server_info: the distributed cache info.
1821
%
1822
%    o image: the image.
1823
%
1824
%    o region: read the pixels from this region of the image.
1825
%
1826
%    o length: the length in bytes of the pixels.
1827
%
1828
%    o pixels: read these pixels from the pixel cache.
1829
%
1830
*/
1831
MagickPrivate MagickOffsetType ReadDistributePixelCachePixels(
1832
  DistributeCacheInfo *server_info,const RectangleInfo *region,
1833
  const MagickSizeType length,unsigned char *magick_restrict pixels)
1834
0
{
1835
0
  MagickOffsetType
1836
0
    count;
1837
1838
0
  unsigned char
1839
0
    message[MagickPathExtent],
1840
0
    *p;
1841
1842
  /*
1843
    Read distributed pixel cache pixels.
1844
  */
1845
0
  assert(server_info != (DistributeCacheInfo *) NULL);
1846
0
  assert(server_info->signature == MagickCoreSignature);
1847
0
  assert(region != (RectangleInfo *) NULL);
1848
0
  assert(pixels != (unsigned char *) NULL);
1849
0
  if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
1850
0
    return(-1);
1851
0
  p=message;
1852
0
  *p++='r';
1853
0
  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1854
0
  p+=(ptrdiff_t) sizeof(server_info->session_key);
1855
0
  (void) memcpy(p,&region->width,sizeof(region->width));
1856
0
  p+=(ptrdiff_t) sizeof(region->width);
1857
0
  (void) memcpy(p,&region->height,sizeof(region->height));
1858
0
  p+=(ptrdiff_t) sizeof(region->height);
1859
0
  (void) memcpy(p,&region->x,sizeof(region->x));
1860
0
  p+=(ptrdiff_t) sizeof(region->x);
1861
0
  (void) memcpy(p,&region->y,sizeof(region->y));
1862
0
  p+=(ptrdiff_t) sizeof(region->y);
1863
0
  (void) memcpy(p,&length,sizeof(length));
1864
0
  p+=(ptrdiff_t) sizeof(length);
1865
0
  count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
1866
0
  if (count != (MagickOffsetType) (p-message))
1867
0
    return(-1);
1868
0
  return(dpc_receive(server_info->file,length,pixels));
1869
0
}
1870

1871
/*
1872
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1873
%                                                                             %
1874
%                                                                             %
1875
%                                                                             %
1876
+   R e l i n q u i s h D i s t r i b u t e P i x e l C a c h e               %
1877
%                                                                             %
1878
%                                                                             %
1879
%                                                                             %
1880
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1881
%
1882
%  RelinquishDistributePixelCache() frees resources acquired with
1883
%  OpenDistributePixelCache().
1884
%
1885
%  The format of the RelinquishDistributePixelCache method is:
1886
%
1887
%      MagickBooleanType RelinquishDistributePixelCache(
1888
%        DistributeCacheInfo *server_info)
1889
%
1890
%  A description of each parameter follows:
1891
%
1892
%    o server_info: the distributed cache info.
1893
%
1894
*/
1895
MagickPrivate MagickBooleanType RelinquishDistributePixelCache(
1896
  DistributeCacheInfo *server_info)
1897
0
{
1898
0
  MagickBooleanType
1899
0
    status;
1900
1901
0
  MagickOffsetType
1902
0
    count;
1903
1904
0
  unsigned char
1905
0
    message[MagickPathExtent],
1906
0
    *p;
1907
1908
  /*
1909
    Delete distributed pixel cache.
1910
  */
1911
0
  assert(server_info != (DistributeCacheInfo *) NULL);
1912
0
  assert(server_info->signature == MagickCoreSignature);
1913
0
  p=message;
1914
0
  *p++='d';
1915
0
  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1916
0
  p+=(ptrdiff_t) sizeof(server_info->session_key);
1917
0
  count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
1918
0
  if (count != (MagickOffsetType) (p-message))
1919
0
    return(MagickFalse);
1920
0
  status=MagickFalse;
1921
0
  count=dpc_receive(server_info->file,sizeof(status),(unsigned char *) &status);
1922
0
  if (count != (MagickOffsetType) sizeof(status))
1923
0
    return(MagickFalse);
1924
0
  return(status);
1925
0
}
1926

1927
/*
1928
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1929
%                                                                             %
1930
%                                                                             %
1931
%                                                                             %
1932
+   W r i t e D i s t r i b u t e P i x e l C a c h e M e t a c o n t e n t   %
1933
%                                                                             %
1934
%                                                                             %
1935
%                                                                             %
1936
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1937
%
1938
%  WriteDistributePixelCacheMetacontents() writes image metacontent to the
1939
%  specified region of the distributed pixel cache.
1940
%
1941
%  The format of the WriteDistributePixelCacheMetacontents method is:
1942
%
1943
%      MagickOffsetType WriteDistributePixelCacheMetacontents(
1944
%        DistributeCacheInfo *server_info,const RectangleInfo *region,
1945
%        const MagickSizeType length,const unsigned char *metacontent)
1946
%
1947
%  A description of each parameter follows:
1948
%
1949
%    o server_info: the distributed cache info.
1950
%
1951
%    o image: the image.
1952
%
1953
%    o region: write the metacontent to this region of the image.
1954
%
1955
%    o length: the length in bytes of the metacontent.
1956
%
1957
%    o metacontent: write these metacontent to the pixel cache.
1958
%
1959
*/
1960
MagickPrivate MagickOffsetType WriteDistributePixelCacheMetacontent(
1961
  DistributeCacheInfo *server_info,const RectangleInfo *region,
1962
  const MagickSizeType length,const unsigned char *metacontent)
1963
0
{
1964
0
  MagickOffsetType
1965
0
    count;
1966
1967
0
  unsigned char
1968
0
    message[MagickPathExtent],
1969
0
    *p;
1970
1971
  /*
1972
    Write distributed pixel cache metacontent.
1973
  */
1974
0
  assert(server_info != (DistributeCacheInfo *) NULL);
1975
0
  assert(server_info->signature == MagickCoreSignature);
1976
0
  assert(region != (RectangleInfo *) NULL);
1977
0
  assert(metacontent != (unsigned char *) NULL);
1978
0
  if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
1979
0
    return(-1);
1980
0
  p=message;
1981
0
  *p++='W';
1982
0
  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1983
0
  p+=(ptrdiff_t) sizeof(server_info->session_key);
1984
0
  (void) memcpy(p,&region->width,sizeof(region->width));
1985
0
  p+=(ptrdiff_t) sizeof(region->width);
1986
0
  (void) memcpy(p,&region->height,sizeof(region->height));
1987
0
  p+=(ptrdiff_t) sizeof(region->height);
1988
0
  (void) memcpy(p,&region->x,sizeof(region->x));
1989
0
  p+=(ptrdiff_t) sizeof(region->x);
1990
0
  (void) memcpy(p,&region->y,sizeof(region->y));
1991
0
  p+=(ptrdiff_t) sizeof(region->y);
1992
0
  (void) memcpy(p,&length,sizeof(length));
1993
0
  p+=(ptrdiff_t) sizeof(length);
1994
0
  count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
1995
0
  if (count != (MagickOffsetType) (p-message))
1996
0
    return(-1);
1997
0
  return(dpc_send(server_info->file,length,metacontent));
1998
0
}
1999

2000
/*
2001
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2002
%                                                                             %
2003
%                                                                             %
2004
%                                                                             %
2005
+   W r i t e D i s t r i b u t e P i x e l C a c h e P i x e l s             %
2006
%                                                                             %
2007
%                                                                             %
2008
%                                                                             %
2009
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2010
%
2011
%  WriteDistributePixelCachePixels() writes image pixels to the specified
2012
%  region of the distributed pixel cache.
2013
%
2014
%  The format of the WriteDistributePixelCachePixels method is:
2015
%
2016
%      MagickBooleanType WriteDistributePixelCachePixels(
2017
%        DistributeCacheInfo *server_info,const RectangleInfo *region,
2018
%        const MagickSizeType length,
2019
%        const unsigned char *magick_restrict pixels)
2020
%
2021
%  A description of each parameter follows:
2022
%
2023
%    o server_info: the distributed cache info.
2024
%
2025
%    o image: the image.
2026
%
2027
%    o region: write the pixels to this region of the image.
2028
%
2029
%    o length: the length in bytes of the pixels.
2030
%
2031
%    o pixels: write these pixels to the pixel cache.
2032
%
2033
*/
2034
MagickPrivate MagickOffsetType WriteDistributePixelCachePixels(
2035
  DistributeCacheInfo *server_info,const RectangleInfo *region,
2036
  const MagickSizeType length,const unsigned char *magick_restrict pixels)
2037
0
{
2038
0
  MagickOffsetType
2039
0
    count;
2040
2041
0
  unsigned char
2042
0
    message[MagickPathExtent],
2043
0
    *p;
2044
2045
  /*
2046
    Write distributed pixel cache pixels.
2047
  */
2048
0
  assert(server_info != (DistributeCacheInfo *) NULL);
2049
0
  assert(server_info->signature == MagickCoreSignature);
2050
0
  assert(region != (RectangleInfo *) NULL);
2051
0
  assert(pixels != (const unsigned char *) NULL);
2052
0
  if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
2053
0
    return(-1);
2054
0
  p=message;
2055
0
  *p++='w';
2056
0
  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
2057
0
  p+=(ptrdiff_t) sizeof(server_info->session_key);
2058
0
  (void) memcpy(p,&region->width,sizeof(region->width));
2059
0
  p+=(ptrdiff_t) sizeof(region->width);
2060
0
  (void) memcpy(p,&region->height,sizeof(region->height));
2061
0
  p+=(ptrdiff_t) sizeof(region->height);
2062
0
  (void) memcpy(p,&region->x,sizeof(region->x));
2063
0
  p+=(ptrdiff_t) sizeof(region->x);
2064
0
  (void) memcpy(p,&region->y,sizeof(region->y));
2065
0
  p+=(ptrdiff_t) sizeof(region->y);
2066
0
  (void) memcpy(p,&length,sizeof(length));
2067
0
  p+=(ptrdiff_t) sizeof(length);
2068
0
  count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
2069
0
  if (count != (MagickOffsetType) (p-message))
2070
0
    return(-1);
2071
0
  return(dpc_send(server_info->file,length,pixels));
2072
0
}