distribute-cache.c revision 8155c1010f07e0f6a2598524957844fc45af15e6
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%                                John Cristy                                  %
22%                                January 2013                                 %
23%                                                                             %
24%                                                                             %
25%  Copyright 1999-2013 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%    http://www.imagemagick.org/script/license.php                            %
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/list.h"
64#include "MagickCore/locale_.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/nt-base-private.h"
67#include "MagickCore/pixel.h"
68#include "MagickCore/policy.h"
69#include "MagickCore/random_.h"
70#include "MagickCore/registry.h"
71#include "MagickCore/splay-tree.h"
72#include "MagickCore/string_.h"
73#include "MagickCore/string-private.h"
74#include "MagickCore/version.h"
75#include "MagickCore/version-private.h"
76#if defined(MAGICKCORE_HAVE_SOCKET)
77#include <netinet/in.h>
78#include <netdb.h>
79#include <sys/socket.h>
80#include <arpa/inet.h>
81#endif
82
83/*
84  Define declarations.
85*/
86#define DPCHostname  "127.0.0.1"
87#define DPCPendingConnections  10
88#define DPCPort  6668
89#define DPCSessionKeyLength  8
90#ifndef MSG_NOSIGNAL
91#  define MSG_NOSIGNAL 0
92#endif
93
94/*
95%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96%                                                                             %
97%                                                                             %
98%                                                                             %
99+   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                       %
100%                                                                             %
101%                                                                             %
102%                                                                             %
103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104%
105%  AcquireDistributeCacheInfo() allocates the DistributeCacheInfo structure.
106%
107%  The format of the AcquireDistributeCacheInfo method is:
108%
109%      DistributeCacheInfo *AcquireDistributeCacheInfo(ExceptionInfo *exception)
110%
111%  A description of each parameter follows:
112%
113%    o exception: return any errors or warnings in this structure.
114%
115*/
116
117static inline MagickSizeType MagickMin(const MagickSizeType x,
118  const MagickSizeType y)
119{
120  if (x < y)
121    return(x);
122  return(y);
123}
124
125static inline MagickOffsetType dpc_read(int file,const MagickSizeType length,
126  unsigned char *restrict message)
127{
128  register MagickOffsetType
129    i;
130
131  ssize_t
132    count;
133
134  count=0;
135  for (i=0; i < (MagickOffsetType) length; i+=count)
136  {
137    count=recv(file,message+i,(size_t) MagickMin(length-i,(MagickSizeType)
138      SSIZE_MAX),0);
139    if (count <= 0)
140      {
141        count=0;
142        if (errno != EINTR)
143          break;
144      }
145  }
146  return(i);
147}
148
149static int ConnectPixelCacheServer(const char *hostname,const int port,
150  size_t *session_key,ExceptionInfo *exception)
151{
152#if defined(MAGICKCORE_HAVE_SOCKET)
153  char
154    service[MaxTextExtent];
155
156  const char
157    *shared_secret;
158
159  int
160    client_socket,
161    status;
162
163  ssize_t
164    count;
165
166  struct addrinfo
167    hint,
168    *result;
169
170  unsigned char
171    secret[MaxTextExtent];
172
173  /*
174    Connect to distributed pixel cache and get session key.
175  */
176  *session_key=0;
177  shared_secret=GetPolicyValue("shared-secret");
178  if (shared_secret == (const char *) NULL)
179    {
180      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
181        "DistributedPixelCache","'%s'","shared secret expected");
182      return(-1);
183    }
184  (void) ResetMagickMemory(&hint,0,sizeof(hint));
185  hint.ai_family=AF_INET;
186  hint.ai_socktype=SOCK_STREAM;
187  hint.ai_flags=AI_PASSIVE;
188  (void) FormatLocaleString(service,MaxTextExtent,"%d",port);
189  status=getaddrinfo(hostname,service,&hint,&result);
190  if (status != 0)
191    {
192      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
193        "DistributedPixelCache","'%s'",hostname);
194      return(-1);
195    }
196  client_socket=socket(result->ai_family,result->ai_socktype,
197    result->ai_protocol);
198  if (client_socket == -1)
199    {
200      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
201        "DistributedPixelCache","'%s'",hostname);
202      return(-1);
203    }
204  status=connect(client_socket,result->ai_addr,result->ai_addrlen);
205  if (status == -1)
206    {
207      (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
208        "DistributedPixelCache","'%s'",hostname);
209      return(-1);
210    }
211  count=recv(client_socket,secret,MaxTextExtent,0);
212  if (count != -1)
213    {
214      StringInfo
215        *nonce;
216
217      nonce=AcquireStringInfo(count);
218      (void) memcpy(GetStringInfoDatum(nonce),secret,(size_t) count);
219      *session_key=GetMagickSignature(nonce);
220      nonce=DestroyStringInfo(nonce);
221    }
222  if (*session_key == 0)
223    {
224      close(client_socket);
225      client_socket=(-1);
226    }
227  return(client_socket);
228#else
229  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
230    "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
231  return(MagickFalse);
232#endif
233}
234
235static char *GetHostname(int *port,ExceptionInfo *exception)
236{
237  char
238    *host,
239    *hosts,
240    **hostlist;
241
242  int
243    argc;
244
245  register ssize_t
246    i;
247
248  static size_t
249    id = 0;
250
251  /*
252    Parse host list (e.g. 192.168.100.1:6668,192.168.100.2:6668).
253  */
254  hosts=(char *) GetImageRegistry(StringRegistryType,"cache:hosts",
255    exception);
256  if (hosts == (char *) NULL)
257    {
258      *port=DPCPort;
259      return(AcquireString(DPCHostname));
260    }
261  (void) SubstituteString(&hosts,","," ");
262  hostlist=StringToArgv(hosts,&argc);
263  hosts=DestroyString(hosts);
264  if (hostlist == (char **) NULL)
265    {
266      *port=DPCPort;
267      return(AcquireString(DPCHostname));
268    }
269  hosts=AcquireString(hostlist[(id++ % (argc-1))+1]);
270  for (i=0; i < (ssize_t) argc; i++)
271    hostlist[i]=DestroyString(hostlist[i]);
272  hostlist=(char **) RelinquishMagickMemory(hostlist);
273  (void) SubstituteString(&hosts,":"," ");
274  hostlist=StringToArgv(hosts,&argc);
275  if (hostlist == (char **) NULL)
276    {
277      *port=DPCPort;
278      return(AcquireString(DPCHostname));
279    }
280  host=AcquireString(hostlist[1]);
281  if (hostlist[2] == (char *) NULL)
282    *port=DPCPort;
283  else
284    *port=StringToLong(hostlist[2]);
285  for (i=0; i < (ssize_t) argc; i++)
286    hostlist[i]=DestroyString(hostlist[i]);
287  hostlist=(char **) RelinquishMagickMemory(hostlist);
288  return(host);
289}
290
291MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
292  ExceptionInfo *exception)
293{
294  char
295    *hostname;
296
297  DistributeCacheInfo
298    *server_info;
299
300  size_t
301    session_key;
302
303  /*
304    Connect to the distributed pixel cache server.
305  */
306  server_info=(DistributeCacheInfo *) AcquireMagickMemory(sizeof(*server_info));
307  if (server_info == (DistributeCacheInfo *) NULL)
308    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
309  (void) ResetMagickMemory(server_info,0,sizeof(*server_info));
310  server_info->signature=MagickSignature;
311  server_info->port=0;
312  hostname=GetHostname(&server_info->port,exception);
313  session_key=0;
314  server_info->file=ConnectPixelCacheServer(hostname,server_info->port,
315    &session_key,exception);
316  server_info->session_key=session_key;
317  (void) CopyMagickString(server_info->hostname,hostname,MaxTextExtent);
318  hostname=DestroyString(hostname);
319  if (server_info->file == -1)
320    server_info=DestroyDistributeCacheInfo(server_info);
321  return(server_info);
322}
323
324/*
325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326%                                                                             %
327%                                                                             %
328%                                                                             %
329+   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                       %
330%                                                                             %
331%                                                                             %
332%                                                                             %
333%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334%
335%  DestroyDistributeCacheInfo() deallocates memory associated with an
336%  DistributeCacheInfo structure.
337%
338%  The format of the DestroyDistributeCacheInfo method is:
339%
340%      DistributeCacheInfo *DestroyDistributeCacheInfo(
341%        DistributeCacheInfo *server_info)
342%
343%  A description of each parameter follows:
344%
345%    o server_info: the distributed cache info.
346%
347*/
348MagickPrivate DistributeCacheInfo *DestroyDistributeCacheInfo(
349  DistributeCacheInfo *server_info)
350{
351  assert(server_info != (DistributeCacheInfo *) NULL);
352  assert(server_info->signature == MagickSignature);
353  if (server_info->file > 0)
354    (void) close(server_info->file);
355  server_info->signature=(~MagickSignature);
356  server_info=(DistributeCacheInfo *) RelinquishMagickMemory(server_info);
357  return(server_info);
358}
359
360/*
361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362%                                                                             %
363%                                                                             %
364%                                                                             %
365+   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                       %
366%                                                                             %
367%                                                                             %
368%                                                                             %
369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370%
371%  DistributePixelCacheServer() waits on the specified port for commands to
372%  create, read, update, or destroy a pixel cache.
373%
374%  The format of the DistributePixelCacheServer() method is:
375%
376%      void DistributePixelCacheServer(const int port)
377%
378%  A description of each parameter follows:
379%
380%    o port: connect the distributed pixel cache at this port.
381%
382%    o exception: return any errors or warnings in this structure.
383%
384*/
385
386static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *registry,
387  int file,const size_t session_key)
388{
389  /*
390    Destroy distributed pixel cache.
391  */
392  return(DeleteNodeFromSplayTree(registry,(const void *) session_key));
393}
394
395static inline MagickOffsetType dpc_send(int file,const MagickSizeType length,
396  const unsigned char *restrict message)
397{
398  MagickOffsetType
399    count;
400
401  register MagickOffsetType
402    i;
403
404  /*
405    Ensure a complete message is sent.
406  */
407  count=0;
408  for (i=0; i < (MagickOffsetType) length; i+=count)
409  {
410    count=(MagickOffsetType) send(file,message+i,(size_t) MagickMin(length-i,
411      (MagickSizeType) SSIZE_MAX),MSG_NOSIGNAL);
412    if (count <= 0)
413      {
414        count=0;
415        if (errno != EINTR)
416          break;
417      }
418  }
419  return(i);
420}
421
422static MagickBooleanType OpenDistributeCache(SplayTreeInfo *registry,
423  int file,const size_t session_key,ExceptionInfo *exception)
424{
425  Image
426    *image;
427
428  MagickBooleanType
429    status;
430
431  MagickOffsetType
432    count;
433
434  MagickSizeType
435    length;
436
437  register unsigned char
438    *p;
439
440  unsigned char
441    message[MaxTextExtent];
442
443  /*
444    Open distributed pixel cache.
445  */
446  image=AcquireImage((ImageInfo *) NULL,exception);
447  if (image == (Image *) NULL)
448    return(MagickFalse);
449  length=sizeof(image->storage_class)+sizeof(image->colorspace)+
450    sizeof(image->alpha_trait)+sizeof(image->mask)+sizeof(image->columns)+
451    sizeof(image->rows)+sizeof(image->number_channels)+MaxPixelChannels*
452    sizeof(*image->channel_map)+sizeof(image->metacontent_extent);
453  count=dpc_read(file,length,message);
454  if (count != (MagickOffsetType) length)
455    return(MagickFalse);
456  /*
457    Deserialize the image attributes.
458  */
459  p=message;
460  (void) memcpy(&image->storage_class,p,sizeof(image->storage_class));
461  p+=sizeof(image->storage_class);
462  (void) memcpy(&image->colorspace,p,sizeof(image->colorspace));
463  p+=sizeof(image->colorspace);
464  (void) memcpy(&image->alpha_trait,p,sizeof(image->alpha_trait));
465  p+=sizeof(image->alpha_trait);
466  (void) memcpy(&image->mask,p,sizeof(image->mask));
467  p+=sizeof(image->mask);
468  (void) memcpy(&image->columns,p,sizeof(image->columns));
469  p+=sizeof(image->columns);
470  (void) memcpy(&image->rows,p,sizeof(image->rows));
471  p+=sizeof(image->rows);
472  (void) memcpy(&image->number_channels,p,sizeof(image->number_channels));
473  p+=sizeof(image->number_channels);
474  (void) memcpy(image->channel_map,p,MaxPixelChannels*
475    sizeof(*image->channel_map));
476  p+=MaxPixelChannels*sizeof(*image->channel_map);
477  (void) memcpy(&image->metacontent_extent,p,sizeof(image->metacontent_extent));
478  p+=sizeof(image->metacontent_extent);
479  if (SyncImagePixelCache(image,exception) == MagickFalse)
480    return(MagickFalse);
481  status=AddValueToSplayTree(registry,(const void *) session_key,image);
482  return(status);
483}
484
485static MagickBooleanType ReadDistributeCacheMetacontent(SplayTreeInfo *registry,
486  int file,const size_t session_key,ExceptionInfo *exception)
487{
488  const unsigned char
489    *metacontent;
490
491  Image
492    *image;
493
494  MagickOffsetType
495    count;
496
497  MagickSizeType
498    length;
499
500  RectangleInfo
501    region;
502
503  register const Quantum
504    *p;
505
506  register unsigned char
507    *q;
508
509  unsigned char
510    message[MaxTextExtent];
511
512  /*
513    Read distributed pixel cache metacontent.
514  */
515  image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
516  if (image == (Image *) NULL)
517    return(MagickFalse);
518  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
519    sizeof(region.y)+sizeof(length);
520  count=dpc_read(file,length,message);
521  if (count != (MagickOffsetType) length)
522    return(MagickFalse);
523  q=message;
524  (void) memcpy(&region.width,q,sizeof(region.width));
525  q+=sizeof(region.width);
526  (void) memcpy(&region.height,q,sizeof(region.height));
527  q+=sizeof(region.height);
528  (void) memcpy(&region.x,q,sizeof(region.x));
529  q+=sizeof(region.x);
530  (void) memcpy(&region.y,q,sizeof(region.y));
531  q+=sizeof(region.y);
532  (void) memcpy(&length,q,sizeof(length));
533  q+=sizeof(length);
534  p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
535    exception);
536  if (p == (const Quantum *) NULL)
537    return(MagickFalse);
538  metacontent=GetVirtualMetacontent(image);
539  count=dpc_send(file,length,metacontent);
540  if (count != (MagickOffsetType) length)
541    return(MagickFalse);
542  return(MagickTrue);
543}
544
545static MagickBooleanType ReadDistributeCachePixels(SplayTreeInfo *registry,
546  int file,const size_t session_key,ExceptionInfo *exception)
547{
548  Image
549    *image;
550
551  MagickOffsetType
552    count;
553
554  MagickSizeType
555    length;
556
557  RectangleInfo
558    region;
559
560  register const Quantum
561    *p;
562
563  register unsigned char
564    *q;
565
566  unsigned char
567    message[MaxTextExtent];
568
569  /*
570    Read distributed pixel cache pixels.
571  */
572  image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
573  if (image == (Image *) NULL)
574    return(MagickFalse);
575  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
576    sizeof(region.y)+sizeof(length);
577  count=dpc_read(file,length,message);
578  if (count != (MagickOffsetType) length)
579    return(MagickFalse);
580  q=message;
581  (void) memcpy(&region.width,q,sizeof(region.width));
582  q+=sizeof(region.width);
583  (void) memcpy(&region.height,q,sizeof(region.height));
584  q+=sizeof(region.height);
585  (void) memcpy(&region.x,q,sizeof(region.x));
586  q+=sizeof(region.x);
587  (void) memcpy(&region.y,q,sizeof(region.y));
588  q+=sizeof(region.y);
589  (void) memcpy(&length,q,sizeof(length));
590  q+=sizeof(length);
591  p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
592    exception);
593  if (p == (const Quantum *) NULL)
594    return(MagickFalse);
595  count=dpc_send(file,length,(unsigned char *) p);
596  if (count != (MagickOffsetType) length)
597    return(MagickFalse);
598  return(MagickTrue);
599}
600
601static void *RelinquishImageRegistry(void *image)
602{
603  return((void *) DestroyImageList((Image *) image));
604}
605
606static MagickBooleanType WriteDistributeCacheMetacontent(
607  SplayTreeInfo *registry,int file,const size_t session_key,
608  ExceptionInfo *exception)
609{
610  Image
611    *image;
612
613  MagickOffsetType
614    count;
615
616  MagickSizeType
617    length;
618
619  RectangleInfo
620    region;
621
622  register Quantum
623    *q;
624
625  register unsigned char
626    *p;
627
628  unsigned char
629    message[MaxTextExtent],
630    *metacontent;
631
632  /*
633    Write distributed pixel cache metacontent.
634  */
635  image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
636  if (image == (Image *) NULL)
637    return(MagickFalse);
638  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
639    sizeof(region.y)+sizeof(length);
640  count=dpc_read(file,length,message);
641  if (count != (MagickOffsetType) length)
642    return(MagickFalse);
643  p=message;
644  (void) memcpy(&region.width,p,sizeof(region.width));
645  p+=sizeof(region.width);
646  (void) memcpy(&region.height,p,sizeof(region.height));
647  p+=sizeof(region.height);
648  (void) memcpy(&region.x,p,sizeof(region.x));
649  p+=sizeof(region.x);
650  (void) memcpy(&region.y,p,sizeof(region.y));
651  p+=sizeof(region.y);
652  (void) memcpy(&length,p,sizeof(length));
653  p+=sizeof(length);
654  q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
655    exception);
656  if (q == (Quantum *) NULL)
657    return(MagickFalse);
658  metacontent=GetAuthenticMetacontent(image);
659  count=dpc_read(file,length,metacontent);
660  if (count != (MagickOffsetType) length)
661    return(MagickFalse);
662  return(SyncAuthenticPixels(image,exception));
663}
664
665static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
666  int file,const size_t session_key,ExceptionInfo *exception)
667{
668  Image
669    *image;
670
671  MagickOffsetType
672    count;
673
674  MagickSizeType
675    length;
676
677  RectangleInfo
678    region;
679
680  register Quantum
681    *q;
682
683  register unsigned char
684    *p;
685
686  unsigned char
687    message[MaxTextExtent];
688
689  /*
690    Write distributed pixel cache pixels.
691  */
692  image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
693  if (image == (Image *) NULL)
694    return(MagickFalse);
695  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
696    sizeof(region.y)+sizeof(length);
697  count=dpc_read(file,length,message);
698  if (count != (MagickOffsetType) length)
699    return(MagickFalse);
700  p=message;
701  (void) memcpy(&region.width,p,sizeof(region.width));
702  p+=sizeof(region.width);
703  (void) memcpy(&region.height,p,sizeof(region.height));
704  p+=sizeof(region.height);
705  (void) memcpy(&region.x,p,sizeof(region.x));
706  p+=sizeof(region.x);
707  (void) memcpy(&region.y,p,sizeof(region.y));
708  p+=sizeof(region.y);
709  (void) memcpy(&length,p,sizeof(length));
710  p+=sizeof(length);
711  q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
712    exception);
713  if (q == (Quantum *) NULL)
714    return(MagickFalse);
715  count=dpc_read(file,length,(unsigned char *) q);
716  if (count != (MagickOffsetType) length)
717    return(MagickFalse);
718  return(SyncAuthenticPixels(image,exception));
719}
720
721static void *DistributePixelCacheClient(void *socket)
722{
723  const char
724    *shared_secret;
725
726  ExceptionInfo
727    *exception;
728
729  int
730    client_socket;
731
732  MagickBooleanType
733    status;
734
735  MagickOffsetType
736    count;
737
738  register unsigned char
739    *p;
740
741  RandomInfo
742    *random_info;
743
744  size_t
745    key,
746    session_key;
747
748  SplayTreeInfo
749    *registry;
750
751  StringInfo
752    *secret;
753
754  unsigned char
755    command,
756    session[2*MaxTextExtent];
757
758  /*
759    Distributed pixel cache client.
760  */
761  shared_secret=GetPolicyValue("shared-secret");
762  if (shared_secret == (const char *) NULL)
763    ThrowFatalException(CacheFatalError,"shared secret expected");
764  p=session;
765  (void) CopyMagickString((char *) p,shared_secret,MaxTextExtent);
766  p+=strlen(shared_secret);
767  random_info=AcquireRandomInfo();
768  secret=GetRandomKey(random_info,DPCSessionKeyLength);
769  (void) memcpy(p,GetStringInfoDatum(secret),DPCSessionKeyLength);
770  session_key=GetMagickSignature(secret);
771  random_info=DestroyRandomInfo(random_info);
772  exception=AcquireExceptionInfo();
773  registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
774    (void *(*)(void *)) NULL,RelinquishImageRegistry);
775  client_socket=(*(int *) socket);
776  count=dpc_send(client_socket,DPCSessionKeyLength,GetStringInfoDatum(secret));
777  secret=DestroyStringInfo(secret);
778  for ( ; ; )
779  {
780    count=dpc_read(client_socket,1,(unsigned char *) &command);
781    if (count <= 0)
782      break;
783    count=dpc_read(client_socket,sizeof(key),(unsigned char *) &key);
784    if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
785      break;
786    status=MagickFalse;
787    switch (command)
788    {
789      case 'o':
790      {
791        status=OpenDistributeCache(registry,client_socket,session_key,
792          exception);
793        count=dpc_send(client_socket,sizeof(status),(unsigned char *) &status);
794        break;
795      }
796      case 'r':
797      {
798        status=ReadDistributeCachePixels(registry,client_socket,session_key,
799          exception);
800        break;
801      }
802      case 'R':
803      {
804        status=ReadDistributeCacheMetacontent(registry,client_socket,
805          session_key,exception);
806        break;
807      }
808      case 'w':
809      {
810        status=WriteDistributeCachePixels(registry,client_socket,session_key,
811          exception);
812        break;
813      }
814      case 'W':
815      {
816        status=WriteDistributeCacheMetacontent(registry,client_socket,
817          session_key,exception);
818        break;
819      }
820      case 'd':
821      {
822        status=DestroyDistributeCache(registry,client_socket,session_key);
823        break;
824      }
825      default:
826        break;
827    }
828    if (status == MagickFalse)
829      break;
830    if (command == 'd')
831      break;
832  }
833  count=dpc_send(client_socket,sizeof(status),(unsigned char *) &status);
834  (void) close(client_socket);
835  exception=DestroyExceptionInfo(exception);
836  registry=DestroySplayTree(registry);
837  return((void *) NULL);
838}
839
840MagickExport void DistributePixelCacheServer(const int port,
841  ExceptionInfo *exception)
842{
843#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
844  char
845    service[MaxTextExtent];
846
847  int
848    server_socket,
849    status;
850
851  pthread_attr_t
852    attributes;
853
854  pthread_t
855    threads;
856
857  register struct addrinfo
858    *p;
859
860  struct addrinfo
861    hint,
862    *result;
863
864  struct sockaddr_in
865    address;
866
867  /*
868    Launch distributed pixel cache server.
869  */
870  (void) ResetMagickMemory(&hint,0,sizeof(hint));
871  hint.ai_family=AF_INET;
872  hint.ai_socktype=SOCK_STREAM;
873  hint.ai_flags=AI_PASSIVE;
874  (void) FormatLocaleString(service,MaxTextExtent,"%d",port);
875  status=getaddrinfo((const char *) NULL,service,&hint,&result);
876  if (status != 0)
877    ThrowFatalException(CacheFatalError,"UnableToListen");
878  server_socket=0;
879  for (p=result; p != (struct addrinfo *) NULL; p=p->ai_next)
880  {
881    int
882      one;
883
884    server_socket=socket(p->ai_family,p->ai_socktype,p->ai_protocol);
885    if (server_socket == -1)
886      continue;
887    one=1;
888    status=setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,&one,(socklen_t)
889      sizeof(one));
890    if (status == -1)
891      continue;
892    status=bind(server_socket,p->ai_addr,p->ai_addrlen);
893    if (status == -1)
894      {
895        (void) close(status);
896        continue;
897      }
898    break;
899  }
900  if (p == (struct addrinfo *) NULL)
901    ThrowFatalException(CacheFatalError,"UnableToBind");
902  freeaddrinfo(result);
903  status=listen(server_socket,DPCPendingConnections);
904  if (status != 0)
905    ThrowFatalException(CacheFatalError,"UnableToListen");
906  pthread_attr_init(&attributes);
907  for ( ; ; )
908  {
909    int
910      client_socket;
911
912    socklen_t
913      length;
914
915    length=(socklen_t) sizeof(address);
916    client_socket=accept(server_socket,(struct sockaddr *) &address,&length);
917    if (client_socket == -1)
918      ThrowFatalException(CacheFatalError,"UnableToEstablishConnection");
919    status=pthread_create(&threads,&attributes,DistributePixelCacheClient,
920      (void *) &client_socket);
921    if (status == -1)
922      ThrowFatalException(CacheFatalError,"UnableToCreateClientThread");
923  }
924  (void) close(server_socket);
925#else
926  ThrowFatalException(MissingDelegateError,"distributed pixel cache");
927#endif
928}
929
930/*
931%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
932%                                                                             %
933%                                                                             %
934%                                                                             %
935+   G e t D i s t r i b u t e C a c h e F i l e                               %
936%                                                                             %
937%                                                                             %
938%                                                                             %
939%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
940%
941%  GetDistributeCacheFile() returns the file associated with this
942%  DistributeCacheInfo structure.
943%
944%  The format of the GetDistributeCacheFile method is:
945%
946%      int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
947%
948%  A description of each parameter follows:
949%
950%    o server_info: the distributed cache info.
951%
952*/
953MagickPrivate int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
954{
955  assert(server_info != (DistributeCacheInfo *) NULL);
956  assert(server_info->signature == MagickSignature);
957  return(server_info->file);
958}
959
960/*
961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
962%                                                                             %
963%                                                                             %
964%                                                                             %
965+   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                       %
966%                                                                             %
967%                                                                             %
968%                                                                             %
969%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
970%
971%  GetDistributeCacheHostname() returns the hostname associated with this
972%  DistributeCacheInfo structure.
973%
974%  The format of the GetDistributeCacheHostname method is:
975%
976%      const char *GetDistributeCacheHostname(
977%        const DistributeCacheInfo *server_info)
978%
979%  A description of each parameter follows:
980%
981%    o server_info: the distributed cache info.
982%
983*/
984MagickPrivate const char *GetDistributeCacheHostname(
985  const DistributeCacheInfo *server_info)
986{
987  assert(server_info != (DistributeCacheInfo *) NULL);
988  assert(server_info->signature == MagickSignature);
989  return(server_info->hostname);
990}
991
992/*
993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994%                                                                             %
995%                                                                             %
996%                                                                             %
997+   G e t D i s t r i b u t e C a c h e P o r t                               %
998%                                                                             %
999%                                                                             %
1000%                                                                             %
1001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002%
1003%  GetDistributeCachePort() returns the port associated with this
1004%  DistributeCacheInfo structure.
1005%
1006%  The format of the GetDistributeCachePort method is:
1007%
1008%      int GetDistributeCachePort(const DistributeCacheInfo *server_info)
1009%
1010%  A description of each parameter follows:
1011%
1012%    o server_info: the distributed cache info.
1013%
1014*/
1015MagickPrivate int GetDistributeCachePort(const DistributeCacheInfo *server_info)
1016{
1017  assert(server_info != (DistributeCacheInfo *) NULL);
1018  assert(server_info->signature == MagickSignature);
1019  return(server_info->port);
1020}
1021
1022/*
1023%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1024%                                                                             %
1025%                                                                             %
1026%                                                                             %
1027+   O p e n D i s t r i b u t e P i x e l C a c h e                           %
1028%                                                                             %
1029%                                                                             %
1030%                                                                             %
1031%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1032%
1033%  OpenDistributePixelCache() opens a pixel cache on a remote server.
1034%
1035%  The format of the OpenDistributePixelCache method is:
1036%
1037%      MagickBooleanType *OpenDistributePixelCache(
1038%        DistributeCacheInfo *server_info,Image *image)
1039%
1040%  A description of each parameter follows:
1041%
1042%    o server_info: the distributed cache info.
1043%
1044%    o image: the image.
1045%
1046*/
1047MagickPrivate MagickBooleanType OpenDistributePixelCache(
1048  DistributeCacheInfo *server_info,Image *image)
1049{
1050  MagickBooleanType
1051    status;
1052
1053  MagickOffsetType
1054    count;
1055
1056  register unsigned char
1057    *p;
1058
1059  unsigned char
1060    message[MaxTextExtent];
1061
1062  /*
1063    Open distributed pixel cache.
1064  */
1065  assert(server_info != (DistributeCacheInfo *) NULL);
1066  assert(server_info->signature == MagickSignature);
1067  assert(image != (Image *) NULL);
1068  assert(image->signature == MagickSignature);
1069  p=message;
1070  *p++='o';  /* open */
1071  /*
1072    Serialize image attributes (see ValidatePixelCacheMorphology()).
1073  */
1074  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1075  p+=sizeof(server_info->session_key);
1076  (void) memcpy(p,&image->storage_class,sizeof(image->storage_class));
1077  p+=sizeof(image->storage_class);
1078  (void) memcpy(p,&image->colorspace,sizeof(image->colorspace));
1079  p+=sizeof(image->colorspace);
1080  (void) memcpy(p,&image->alpha_trait,sizeof(image->alpha_trait));
1081  p+=sizeof(image->alpha_trait);
1082  (void) memcpy(p,&image->mask,sizeof(image->mask));
1083  p+=sizeof(image->mask);
1084  (void) memcpy(p,&image->columns,sizeof(image->columns));
1085  p+=sizeof(image->columns);
1086  (void) memcpy(p,&image->rows,sizeof(image->rows));
1087  p+=sizeof(image->rows);
1088  (void) memcpy(p,&image->number_channels,sizeof(image->number_channels));
1089  p+=sizeof(image->number_channels);
1090  (void) memcpy(p,image->channel_map,MaxPixelChannels*
1091    sizeof(*image->channel_map));
1092  p+=MaxPixelChannels*sizeof(*image->channel_map);
1093  (void) memcpy(p,&image->metacontent_extent,sizeof(image->metacontent_extent));
1094  p+=sizeof(image->metacontent_extent);
1095  count=dpc_send(server_info->file,p-message,message);
1096  if (count != (MagickOffsetType) (p-message))
1097    return(MagickFalse);
1098  status=MagickFalse;
1099  count=dpc_read(server_info->file,sizeof(status),(unsigned char *) &status);
1100  if (count != (MagickOffsetType) sizeof(status))
1101    return(MagickFalse);
1102  return(status);
1103}
1104
1105/*
1106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1107%                                                                             %
1108%                                                                             %
1109%                                                                             %
1110+   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     %
1111%                                                                             %
1112%                                                                             %
1113%                                                                             %
1114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115%
1116%  ReadDistributePixelCacheMetacontents() reads metacontent from the specified
1117%  region of the distributed pixel cache.
1118%
1119%  The format of the ReadDistributePixelCacheMetacontents method is:
1120%
1121%      MagickOffsetType ReadDistributePixelCacheMetacontents(
1122%        DistributeCacheInfo *server_info,const RectangleInfo *region,
1123%        const MagickSizeType length,unsigned char *metacontent)
1124%
1125%  A description of each parameter follows:
1126%
1127%    o server_info: the distributed cache info.
1128%
1129%    o image: the image.
1130%
1131%    o region: read the metacontent from this region of the image.
1132%
1133%    o length: the length in bytes of the metacontent.
1134%
1135%    o metacontent: read these metacontent from the pixel cache.
1136%
1137*/
1138MagickPrivate MagickOffsetType ReadDistributePixelCacheMetacontent(
1139  DistributeCacheInfo *server_info,const RectangleInfo *region,
1140  const MagickSizeType length,unsigned char *metacontent)
1141{
1142  MagickOffsetType
1143    count;
1144
1145  register unsigned char
1146    *p;
1147
1148  unsigned char
1149    message[MaxTextExtent];
1150
1151  /*
1152    Read distributed pixel cache metacontent.
1153  */
1154  assert(server_info != (DistributeCacheInfo *) NULL);
1155  assert(server_info->signature == MagickSignature);
1156  assert(region != (RectangleInfo *) NULL);
1157  assert(metacontent != (unsigned char *) NULL);
1158  if (length > (MagickSizeType) SSIZE_MAX)
1159    return(-1);
1160  p=message;
1161  *p++='R';
1162  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1163  p+=sizeof(server_info->session_key);
1164  (void) memcpy(p,&region->width,sizeof(region->width));
1165  p+=sizeof(region->width);
1166  (void) memcpy(p,&region->height,sizeof(region->height));
1167  p+=sizeof(region->height);
1168  (void) memcpy(p,&region->x,sizeof(region->x));
1169  p+=sizeof(region->x);
1170  (void) memcpy(p,&region->y,sizeof(region->y));
1171  p+=sizeof(region->y);
1172  (void) memcpy(p,&length,sizeof(length));
1173  p+=sizeof(length);
1174  count=dpc_send(server_info->file,p-message,message);
1175  if (count != (MagickOffsetType) (p-message))
1176    return(-1);
1177  return(dpc_read(server_info->file,length,metacontent));
1178}
1179
1180/*
1181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182%                                                                             %
1183%                                                                             %
1184%                                                                             %
1185+   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               %
1186%                                                                             %
1187%                                                                             %
1188%                                                                             %
1189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1190%
1191%  ReadDistributePixelCachePixels() reads pixels from the specified region of
1192%  the distributed pixel cache.
1193%
1194%  The format of the ReadDistributePixelCachePixels method is:
1195%
1196%      MagickOffsetType ReadDistributePixelCachePixels(
1197%        DistributeCacheInfo *server_info,const RectangleInfo *region,
1198%        const MagickSizeType length,unsigned char *pixels)
1199%
1200%  A description of each parameter follows:
1201%
1202%    o server_info: the distributed cache info.
1203%
1204%    o image: the image.
1205%
1206%    o region: read the pixels from this region of the image.
1207%
1208%    o length: the length in bytes of the pixels.
1209%
1210%    o pixels: read these pixels from the pixel cache.
1211%
1212*/
1213MagickPrivate MagickOffsetType ReadDistributePixelCachePixels(
1214  DistributeCacheInfo *server_info,const RectangleInfo *region,
1215  const MagickSizeType length,unsigned char *pixels)
1216{
1217  MagickOffsetType
1218    count;
1219
1220  register unsigned char
1221    *p;
1222
1223  unsigned char
1224    message[MaxTextExtent];
1225
1226  /*
1227    Read distributed pixel cache pixels.
1228  */
1229  assert(server_info != (DistributeCacheInfo *) NULL);
1230  assert(server_info->signature == MagickSignature);
1231  assert(region != (RectangleInfo *) NULL);
1232  assert(pixels != (unsigned char *) NULL);
1233  if (length > (MagickSizeType) SSIZE_MAX)
1234    return(-1);
1235  p=message;
1236  *p++='r';
1237  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1238  p+=sizeof(server_info->session_key);
1239  (void) memcpy(p,&region->width,sizeof(region->width));
1240  p+=sizeof(region->width);
1241  (void) memcpy(p,&region->height,sizeof(region->height));
1242  p+=sizeof(region->height);
1243  (void) memcpy(p,&region->x,sizeof(region->x));
1244  p+=sizeof(region->x);
1245  (void) memcpy(p,&region->y,sizeof(region->y));
1246  p+=sizeof(region->y);
1247  (void) memcpy(p,&length,sizeof(length));
1248  p+=sizeof(length);
1249  count=dpc_send(server_info->file,p-message,message);
1250  if (count != (MagickOffsetType) (p-message))
1251    return(-1);
1252  return(dpc_read(server_info->file,length,pixels));
1253}
1254
1255/*
1256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1257%                                                                             %
1258%                                                                             %
1259%                                                                             %
1260+   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               %
1261%                                                                             %
1262%                                                                             %
1263%                                                                             %
1264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1265%
1266%  RelinquishDistributePixelCache() frees resources acquired with
1267%  OpenDistributePixelCache().
1268%
1269%  The format of the RelinquishDistributePixelCache method is:
1270%
1271%      MagickBooleanType RelinquishDistributePixelCache(
1272%        DistributeCacheInfo *server_info)
1273%
1274%  A description of each parameter follows:
1275%
1276%    o server_info: the distributed cache info.
1277%
1278*/
1279MagickPrivate MagickBooleanType RelinquishDistributePixelCache(
1280  DistributeCacheInfo *server_info)
1281{
1282  MagickBooleanType
1283    status;
1284
1285  MagickOffsetType
1286    count;
1287
1288  register unsigned char
1289    *p;
1290
1291  unsigned char
1292    message[MaxTextExtent];
1293
1294  /*
1295    Delete distributed pixel cache.
1296  */
1297  assert(server_info != (DistributeCacheInfo *) NULL);
1298  assert(server_info->signature == MagickSignature);
1299  p=message;
1300  *p++='d';
1301  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1302  p+=sizeof(server_info->session_key);
1303  count=dpc_send(server_info->file,p-message,message);
1304  if (count != (MagickOffsetType) (p-message))
1305    return(MagickFalse);
1306  count=dpc_read(server_info->file,sizeof(status),(unsigned char *) &status);
1307  if (count != (MagickOffsetType) sizeof(status))
1308    return(MagickFalse);
1309  return(status);
1310}
1311
1312/*
1313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1314%                                                                             %
1315%                                                                             %
1316%                                                                             %
1317+   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   %
1318%                                                                             %
1319%                                                                             %
1320%                                                                             %
1321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1322%
1323%  WriteDistributePixelCacheMetacontents() writes image metacontent to the
1324%  specified region of the distributed pixel cache.
1325%
1326%  The format of the WriteDistributePixelCacheMetacontents method is:
1327%
1328%      MagickOffsetType WriteDistributePixelCacheMetacontents(
1329%        DistributeCacheInfo *server_info,const RectangleInfo *region,
1330%        const MagickSizeType length,const unsigned char *metacontent)
1331%
1332%  A description of each parameter follows:
1333%
1334%    o server_info: the distributed cache info.
1335%
1336%    o image: the image.
1337%
1338%    o region: write the metacontent to this region of the image.
1339%
1340%    o length: the length in bytes of the metacontent.
1341%
1342%    o metacontent: write these metacontent to the pixel cache.
1343%
1344*/
1345MagickPrivate MagickOffsetType WriteDistributePixelCacheMetacontent(
1346  DistributeCacheInfo *server_info,const RectangleInfo *region,
1347  const MagickSizeType length,const unsigned char *metacontent)
1348{
1349  MagickOffsetType
1350    count;
1351
1352  register unsigned char
1353    *p;
1354
1355  unsigned char
1356    message[MaxTextExtent];
1357
1358  /*
1359    Write distributed pixel cache metacontent.
1360  */
1361  assert(server_info != (DistributeCacheInfo *) NULL);
1362  assert(server_info->signature == MagickSignature);
1363  assert(region != (RectangleInfo *) NULL);
1364  assert(metacontent != (unsigned char *) NULL);
1365  if (length > (MagickSizeType) SSIZE_MAX)
1366    return(-1);
1367  p=message;
1368  *p++='W';
1369  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1370  p+=sizeof(server_info->session_key);
1371  (void) memcpy(p,&region->width,sizeof(region->width));
1372  p+=sizeof(region->width);
1373  (void) memcpy(p,&region->height,sizeof(region->height));
1374  p+=sizeof(region->height);
1375  (void) memcpy(p,&region->x,sizeof(region->x));
1376  p+=sizeof(region->x);
1377  (void) memcpy(p,&region->y,sizeof(region->y));
1378  p+=sizeof(region->y);
1379  (void) memcpy(p,&length,sizeof(length));
1380  p+=sizeof(length);
1381  count=dpc_send(server_info->file,p-message,message);
1382  if (count != (MagickOffsetType) (p-message))
1383    return(-1);
1384  return(dpc_send(server_info->file,length,metacontent));
1385}
1386
1387/*
1388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1389%                                                                             %
1390%                                                                             %
1391%                                                                             %
1392+   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             %
1393%                                                                             %
1394%                                                                             %
1395%                                                                             %
1396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1397%
1398%  WriteDistributePixelCachePixels() writes image pixels to the specified
1399%  region of the distributed pixel cache.
1400%
1401%  The format of the WriteDistributePixelCachePixels method is:
1402%
1403%      MagickBooleanType WriteDistributePixelCachePixels(
1404%        DistributeCacheInfo *server_info,const RectangleInfo *region,
1405%        const MagickSizeType length,const unsigned char *pixels)
1406%
1407%  A description of each parameter follows:
1408%
1409%    o server_info: the distributed cache info.
1410%
1411%    o image: the image.
1412%
1413%    o region: write the pixels to this region of the image.
1414%
1415%    o length: the length in bytes of the pixels.
1416%
1417%    o pixels: write these pixels to the pixel cache.
1418%
1419*/
1420MagickPrivate MagickOffsetType WriteDistributePixelCachePixels(
1421  DistributeCacheInfo *server_info,const RectangleInfo *region,
1422  const MagickSizeType length,const unsigned char *pixels)
1423{
1424  MagickOffsetType
1425    count;
1426
1427  register unsigned char
1428    *p;
1429
1430  unsigned char
1431    message[MaxTextExtent];
1432
1433  /*
1434    Write distributed pixel cache pixels.
1435  */
1436  assert(server_info != (DistributeCacheInfo *) NULL);
1437  assert(server_info->signature == MagickSignature);
1438  assert(region != (RectangleInfo *) NULL);
1439  assert(pixels != (const unsigned char *) NULL);
1440  if (length > (MagickSizeType) SSIZE_MAX)
1441    return(-1);
1442  p=message;
1443  *p++='w';
1444  (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1445  p+=sizeof(server_info->session_key);
1446  (void) memcpy(p,&region->width,sizeof(region->width));
1447  p+=sizeof(region->width);
1448  (void) memcpy(p,&region->height,sizeof(region->height));
1449  p+=sizeof(region->height);
1450  (void) memcpy(p,&region->x,sizeof(region->x));
1451  p+=sizeof(region->x);
1452  (void) memcpy(p,&region->y,sizeof(region->y));
1453  p+=sizeof(region->y);
1454  (void) memcpy(p,&length,sizeof(length));
1455  p+=sizeof(length);
1456  count=dpc_send(server_info->file,p-message,message);
1457  if (count != (MagickOffsetType) (p-message))
1458    return(-1);
1459  return(dpc_send(server_info->file,length,pixels));
1460}
1461