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