% MagickCore Distributed Pixel Cache Methods %
% %
% Software Design %
-% John Cristy %
+% Cristy %
% January 2013 %
% %
% %
-% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
% A distributed pixel cache is an extension of the traditional pixel cache
% available on a single host. The distributed pixel cache may span multiple
% servers so that it can grow in size and transactional capacity to support
-% very large images. Start up the pixel cache server on one or more machines.
+% very large images. Start up the pixel cache server on one or more machines.
% When you read or operate on an image and the local pixel cache resources are
% exhausted, ImageMagick contacts one or more of these remote pixel servers to
% store or retrieve pixels.
Include declarations.
*/
#include "MagickCore/studio.h"
+#include "MagickCore/cache.h"
+#include "MagickCore/cache-private.h"
+#include "MagickCore/distribute-cache.h"
+#include "MagickCore/distribute-cache-private.h"
+#include "MagickCore/exception.h"
+#include "MagickCore/exception-private.h"
+#include "MagickCore/geometry.h"
+#include "MagickCore/image.h"
+#include "MagickCore/list.h"
+#include "MagickCore/locale_.h"
#include "MagickCore/memory_.h"
-#if defined(MAGICKCORE_HAVE_SOCKET)
-#include <sys/socket.h>
+#include "MagickCore/nt-base-private.h"
+#include "MagickCore/pixel.h"
+#include "MagickCore/policy.h"
+#include "MagickCore/random_.h"
+#include "MagickCore/registry.h"
+#include "MagickCore/splay-tree.h"
+#include "MagickCore/string_.h"
+#include "MagickCore/string-private.h"
+#include "MagickCore/version.h"
+#include "MagickCore/version-private.h"
+#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/socket.h>
#include <arpa/inet.h>
+#else
+#undef send
+#undef recv
+#define send(file,buffer,length,flags) 0
+#define recv(file,buffer,length,flags) 0
+#endif
+\f
+/*
+ Define declarations.
+*/
+#define DPCHostname "127.0.0.1"
+#define DPCPendingConnections 10
+#define DPCPort 6668
+#define DPCSessionKeyLength 8
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
#endif
\f
/*
% %
% %
% %
-+ P i x e l C a c h e S e r v e r %
++ 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 %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% PixelCacheServer() waits on the specified port for commands to create, read,
-% update, or destroy a pixel cache.
+% AcquireDistributeCacheInfo() allocates the DistributeCacheInfo structure.
%
-% The format of the PixelCacheServer() method is:
+% The format of the AcquireDistributeCacheInfo method is:
%
-% void PixelCacheServer(const size_t port)
+% DistributeCacheInfo *AcquireDistributeCacheInfo(ExceptionInfo *exception)
%
% A description of each parameter follows:
%
-% o port: connect the distributed pixel cache at this port.
+% o exception: return any errors or warnings in this structure.
%
*/
-MagickExport void PixelCacheServer(const size_t port)
+
+static inline MagickSizeType MagickMin(const MagickSizeType x,
+ const MagickSizeType y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static inline MagickOffsetType dpc_read(int file,const MagickSizeType length,
+ unsigned char *restrict message)
{
-#if defined(MAGICKCORE_HAVE_SOCKET)
+ register MagickOffsetType
+ i;
+
+ ssize_t
+ count;
+
+#if !defined(MAGICKCORE_HAVE_SOCKET) || !defined(MAGICKCORE_THREAD_SUPPORT)
+ magick_unreferenced(file);
+ magick_unreferenced(message);
+#endif
+
+ count=0;
+ for (i=0; i < (MagickOffsetType) length; i+=count)
+ {
+ count=recv(file,message+i,(size_t) MagickMin(length-i,(MagickSizeType)
+ SSIZE_MAX),0);
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ }
+ return(i);
+}
+
+static int ConnectPixelCacheServer(const char *hostname,const int port,
+ size_t *session_key,ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
char
- buffer[MaxTextExtent];
+ service[MaxTextExtent];
+
+ const char
+ *shared_secret;
int
- cache_socket,
- cache_client,
+ client_socket,
status;
- socklen_t
- length,
- one;
-
ssize_t
count;
- struct sockaddr_in
- address;
+ struct addrinfo
+ hint,
+ *result;
- cache_socket=socket(AF_INET,SOCK_STREAM,0);
- if (cache_socket == -1)
+ unsigned char
+ secret[MaxTextExtent];
+
+ /*
+ Connect to distributed pixel cache and get session key.
+ */
+ *session_key=0;
+ shared_secret=GetPolicyValue("shared-secret");
+ if (shared_secret == (const char *) NULL)
{
- perror("Distributed pixel cache: server socket");
- exit(1);
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "DistributedPixelCache","'%s'","shared secret expected");
+ return(-1);
}
- one=1;
- status=setsockopt(cache_socket,SOL_SOCKET,SO_REUSEADDR,&one,(socklen_t)
- sizeof(one));
- if (status == -1)
+ (void) ResetMagickMemory(&hint,0,sizeof(hint));
+ hint.ai_family=AF_INET;
+ hint.ai_socktype=SOCK_STREAM;
+ hint.ai_flags=AI_PASSIVE;
+ (void) FormatLocaleString(service,MaxTextExtent,"%d",port);
+ status=getaddrinfo(hostname,service,&hint,&result);
+ if (status != 0)
{
- perror("Distributed pixel cache: server setsockopt");
- exit(1);
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "DistributedPixelCache","'%s'",hostname);
+ return(-1);
}
- (void) ResetMagickMemory(&address,0,sizeof(address));
- address.sin_family=AF_INET;
- address.sin_port=htons(port);
- address.sin_addr.s_addr=INADDR_ANY;
- status=bind(cache_socket,(struct sockaddr *) &address,(socklen_t)
- sizeof(address));
- if (status == -1)
+ client_socket=socket(result->ai_family,result->ai_socktype,
+ result->ai_protocol);
+ if (client_socket == -1)
{
- perror("Distributed pixel cache: server bind");
- exit(1);
+ (void) close(client_socket);
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "DistributedPixelCache","'%s'",hostname);
+ return(-1);
}
- status=listen(cache_socket,5);
+ status=connect(client_socket,result->ai_addr,result->ai_addrlen);
if (status == -1)
{
- perror("Distributed pixel cache: server listen");
- exit(1);
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "DistributedPixelCache","'%s'",hostname);
+ return(-1);
+ }
+ count=recv(client_socket,secret,MaxTextExtent,0);
+ if (count != -1)
+ {
+ StringInfo
+ *nonce;
+
+ nonce=AcquireStringInfo(count);
+ (void) memcpy(GetStringInfoDatum(nonce),secret,(size_t) count);
+ *session_key=GetMagickSignature(nonce);
+ nonce=DestroyStringInfo(nonce);
+ }
+ if (*session_key == 0)
+ {
+ close(client_socket);
+ client_socket=(-1);
+ }
+ return(client_socket);
+#else
+ (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
+ "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
+ return(MagickFalse);
+#endif
+}
+
+static char *GetHostname(int *port,ExceptionInfo *exception)
+{
+ char
+ *host,
+ *hosts,
+ **hostlist;
+
+ int
+ argc;
+
+ register ssize_t
+ i;
+
+ static size_t
+ id = 0;
+
+ /*
+ Parse host list (e.g. 192.168.100.1:6668,192.168.100.2:6668).
+ */
+ hosts=(char *) GetImageRegistry(StringRegistryType,"cache:hosts",exception);
+ if (hosts == (char *) NULL)
+ {
+ *port=DPCPort;
+ return(AcquireString(DPCHostname));
+ }
+ (void) SubstituteString(&hosts,","," ");
+ hostlist=StringToArgv(hosts,&argc);
+ hosts=DestroyString(hosts);
+ if (hostlist == (char **) NULL)
+ {
+ *port=DPCPort;
+ return(AcquireString(DPCHostname));
}
- (void) fprintf(stdout,
- "Distributed pixel cache server: waiting for client on port %d\n",(int)
- port);
- (void) fflush(stdout);
+ hosts=AcquireString(hostlist[(id++ % (argc-1))+1]);
+ for (i=0; i < (ssize_t) argc; i++)
+ hostlist[i]=DestroyString(hostlist[i]);
+ hostlist=(char **) RelinquishMagickMemory(hostlist);
+ (void) SubstituteString(&hosts,":"," ");
+ hostlist=StringToArgv(hosts,&argc);
+ if (hostlist == (char **) NULL)
+ {
+ *port=DPCPort;
+ return(AcquireString(DPCHostname));
+ }
+ host=AcquireString(hostlist[1]);
+ if (hostlist[2] == (char *) NULL)
+ *port=DPCPort;
+ else
+ *port=StringToLong(hostlist[2]);
+ for (i=0; i < (ssize_t) argc; i++)
+ hostlist[i]=DestroyString(hostlist[i]);
+ hostlist=(char **) RelinquishMagickMemory(hostlist);
+ return(host);
+}
+
+MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
+ ExceptionInfo *exception)
+{
+ char
+ *hostname;
+
+ DistributeCacheInfo
+ *server_info;
+
+ size_t
+ session_key;
+
+ /*
+ Connect to the distributed pixel cache server.
+ */
+ server_info=(DistributeCacheInfo *) AcquireMagickMemory(sizeof(*server_info));
+ if (server_info == (DistributeCacheInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(server_info,0,sizeof(*server_info));
+ server_info->signature=MagickSignature;
+ server_info->port=0;
+ hostname=GetHostname(&server_info->port,exception);
+ session_key=0;
+ server_info->file=ConnectPixelCacheServer(hostname,server_info->port,
+ &session_key,exception);
+ server_info->session_key=session_key;
+ (void) CopyMagickString(server_info->hostname,hostname,MaxTextExtent);
+ hostname=DestroyString(hostname);
+ if (server_info->file == -1)
+ server_info=DestroyDistributeCacheInfo(server_info);
+ server_info->debug=IsEventLogging();
+ return(server_info);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyDistributeCacheInfo() deallocates memory associated with an
+% DistributeCacheInfo structure.
+%
+% The format of the DestroyDistributeCacheInfo method is:
+%
+% DistributeCacheInfo *DestroyDistributeCacheInfo(
+% DistributeCacheInfo *server_info)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+*/
+MagickPrivate DistributeCacheInfo *DestroyDistributeCacheInfo(
+ DistributeCacheInfo *server_info)
+{
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ if (server_info->file > 0)
+ (void) close(server_info->file);
+ server_info->signature=(~MagickSignature);
+ server_info=(DistributeCacheInfo *) RelinquishMagickMemory(server_info);
+ return(server_info);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DistributePixelCacheServer() waits on the specified port for commands to
+% create, read, update, or destroy a pixel cache.
+%
+% The format of the DistributePixelCacheServer() method is:
+%
+% void DistributePixelCacheServer(const int port)
+%
+% A description of each parameter follows:
+%
+% o port: connect the distributed pixel cache at this port.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *registry,
+ int magick_unused(file),const size_t session_key)
+{
+ /*
+ Destroy distributed pixel cache.
+ */
+ magick_unreferenced(file);
+ return(DeleteNodeFromSplayTree(registry,(const void *) session_key));
+}
+
+static inline MagickOffsetType dpc_send(int file,const MagickSizeType length,
+ const unsigned char *restrict message)
+{
+ MagickOffsetType
+ count;
+
+ register MagickOffsetType
+ i;
+
+#if !defined(MAGICKCORE_HAVE_SOCKET) || !defined(MAGICKCORE_THREAD_SUPPORT)
+ magick_unreferenced(file);
+ magick_unreferenced(message);
+#endif
+
+ /*
+ Ensure a complete message is sent.
+ */
+ count=0;
+ for (i=0; i < (MagickOffsetType) length; i+=count)
+ {
+ count=(MagickOffsetType) send(file,message+i,(size_t) MagickMin(length-i,
+ (MagickSizeType) SSIZE_MAX),MSG_NOSIGNAL);
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ }
+ return(i);
+}
+
+static MagickBooleanType OpenDistributeCache(SplayTreeInfo *registry,int file,
+ const size_t session_key,ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ count;
+
+ MagickSizeType
+ length;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Open distributed pixel cache.
+ */
+ image=AcquireImage((ImageInfo *) NULL,exception);
+ if (image == (Image *) NULL)
+ return(MagickFalse);
+ length=sizeof(image->storage_class)+sizeof(image->colorspace)+
+ sizeof(image->alpha_trait)+sizeof(image->read_mask)+
+ sizeof(image->write_mask)+sizeof(image->columns)+sizeof(image->rows)+
+ sizeof(image->number_channels)+MaxPixelChannels*sizeof(*image->channel_map)+
+ sizeof(image->metacontent_extent);
+ count=dpc_read(file,length,message);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ /*
+ Deserialize the image attributes.
+ */
+ p=message;
+ (void) memcpy(&image->storage_class,p,sizeof(image->storage_class));
+ p+=sizeof(image->storage_class);
+ (void) memcpy(&image->colorspace,p,sizeof(image->colorspace));
+ p+=sizeof(image->colorspace);
+ (void) memcpy(&image->alpha_trait,p,sizeof(image->alpha_trait));
+ p+=sizeof(image->alpha_trait);
+ (void) memcpy(&image->read_mask,p,sizeof(image->read_mask));
+ p+=sizeof(image->read_mask);
+ (void) memcpy(&image->write_mask,p,sizeof(image->write_mask));
+ p+=sizeof(image->write_mask);
+ (void) memcpy(&image->columns,p,sizeof(image->columns));
+ p+=sizeof(image->columns);
+ (void) memcpy(&image->rows,p,sizeof(image->rows));
+ p+=sizeof(image->rows);
+ (void) memcpy(&image->number_channels,p,sizeof(image->number_channels));
+ p+=sizeof(image->number_channels);
+ (void) memcpy(image->channel_map,p,MaxPixelChannels*
+ sizeof(*image->channel_map));
+ p+=MaxPixelChannels*sizeof(*image->channel_map);
+ (void) memcpy(&image->metacontent_extent,p,sizeof(image->metacontent_extent));
+ p+=sizeof(image->metacontent_extent);
+ if (SyncImagePixelCache(image,exception) == MagickFalse)
+ return(MagickFalse);
+ status=AddValueToSplayTree(registry,(const void *) session_key,image);
+ return(status);
+}
+
+static MagickBooleanType ReadDistributeCacheMetacontent(SplayTreeInfo *registry,
+ int file,const size_t session_key,ExceptionInfo *exception)
+{
+ const unsigned char
+ *metacontent;
+
+ Image
+ *image;
+
+ MagickOffsetType
+ count;
+
+ MagickSizeType
+ length;
+
+ RectangleInfo
+ region;
+
+ register const Quantum
+ *p;
+
+ register unsigned char
+ *q;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Read distributed pixel cache metacontent.
+ */
+ image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
+ if (image == (Image *) NULL)
+ return(MagickFalse);
+ length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
+ sizeof(region.y)+sizeof(length);
+ count=dpc_read(file,length,message);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ q=message;
+ (void) memcpy(®ion.width,q,sizeof(region.width));
+ q+=sizeof(region.width);
+ (void) memcpy(®ion.height,q,sizeof(region.height));
+ q+=sizeof(region.height);
+ (void) memcpy(®ion.x,q,sizeof(region.x));
+ q+=sizeof(region.x);
+ (void) memcpy(®ion.y,q,sizeof(region.y));
+ q+=sizeof(region.y);
+ (void) memcpy(&length,q,sizeof(length));
+ q+=sizeof(length);
+ p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
+ exception);
+ if (p == (const Quantum *) NULL)
+ return(MagickFalse);
+ metacontent=(const unsigned char *) GetVirtualMetacontent(image);
+ count=dpc_send(file,length,metacontent);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+static MagickBooleanType ReadDistributeCachePixels(SplayTreeInfo *registry,
+ int file,const size_t session_key,ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ MagickOffsetType
+ count;
+
+ MagickSizeType
+ length;
+
+ RectangleInfo
+ region;
+
+ register const Quantum
+ *p;
+
+ register unsigned char
+ *q;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Read distributed pixel cache pixels.
+ */
+ image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
+ if (image == (Image *) NULL)
+ return(MagickFalse);
+ length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
+ sizeof(region.y)+sizeof(length);
+ count=dpc_read(file,length,message);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ q=message;
+ (void) memcpy(®ion.width,q,sizeof(region.width));
+ q+=sizeof(region.width);
+ (void) memcpy(®ion.height,q,sizeof(region.height));
+ q+=sizeof(region.height);
+ (void) memcpy(®ion.x,q,sizeof(region.x));
+ q+=sizeof(region.x);
+ (void) memcpy(®ion.y,q,sizeof(region.y));
+ q+=sizeof(region.y);
+ (void) memcpy(&length,q,sizeof(length));
+ q+=sizeof(length);
+ p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
+ exception);
+ if (p == (const Quantum *) NULL)
+ return(MagickFalse);
+ count=dpc_send(file,length,(unsigned char *) p);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+static void *RelinquishImageRegistry(void *image)
+{
+ return((void *) DestroyImageList((Image *) image));
+}
+
+static MagickBooleanType WriteDistributeCacheMetacontent(
+ SplayTreeInfo *registry,int file,const size_t session_key,
+ ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ MagickOffsetType
+ count;
+
+ MagickSizeType
+ length;
+
+ RectangleInfo
+ region;
+
+ register Quantum
+ *q;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent],
+ *metacontent;
+
+ /*
+ Write distributed pixel cache metacontent.
+ */
+ image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
+ if (image == (Image *) NULL)
+ return(MagickFalse);
+ length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
+ sizeof(region.y)+sizeof(length);
+ count=dpc_read(file,length,message);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ p=message;
+ (void) memcpy(®ion.width,p,sizeof(region.width));
+ p+=sizeof(region.width);
+ (void) memcpy(®ion.height,p,sizeof(region.height));
+ p+=sizeof(region.height);
+ (void) memcpy(®ion.x,p,sizeof(region.x));
+ p+=sizeof(region.x);
+ (void) memcpy(®ion.y,p,sizeof(region.y));
+ p+=sizeof(region.y);
+ (void) memcpy(&length,p,sizeof(length));
+ p+=sizeof(length);
+ q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
+ exception);
+ if (q == (Quantum *) NULL)
+ return(MagickFalse);
+ metacontent=(unsigned char *) GetAuthenticMetacontent(image);
+ count=dpc_read(file,length,metacontent);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ return(SyncAuthenticPixels(image,exception));
+}
+
+static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
+ int file,const size_t session_key,ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ MagickOffsetType
+ count;
+
+ MagickSizeType
+ length;
+
+ RectangleInfo
+ region;
+
+ register Quantum
+ *q;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Write distributed pixel cache pixels.
+ */
+ image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
+ if (image == (Image *) NULL)
+ return(MagickFalse);
+ length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
+ sizeof(region.y)+sizeof(length);
+ count=dpc_read(file,length,message);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ p=message;
+ (void) memcpy(®ion.width,p,sizeof(region.width));
+ p+=sizeof(region.width);
+ (void) memcpy(®ion.height,p,sizeof(region.height));
+ p+=sizeof(region.height);
+ (void) memcpy(®ion.x,p,sizeof(region.x));
+ p+=sizeof(region.x);
+ (void) memcpy(®ion.y,p,sizeof(region.y));
+ p+=sizeof(region.y);
+ (void) memcpy(&length,p,sizeof(length));
+ p+=sizeof(length);
+ q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
+ exception);
+ if (q == (Quantum *) NULL)
+ return(MagickFalse);
+ count=dpc_read(file,length,(unsigned char *) q);
+ if (count != (MagickOffsetType) length)
+ return(MagickFalse);
+ return(SyncAuthenticPixels(image,exception));
+}
+
+static void *DistributePixelCacheClient(void *socket)
+{
+ const char
+ *shared_secret;
+
+ ExceptionInfo
+ *exception;
+
+ int
+ client_socket;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ count;
+
+ register unsigned char
+ *p;
+
+ RandomInfo
+ *random_info;
+
+ size_t
+ key,
+ session_key;
+
+ SplayTreeInfo
+ *registry;
+
+ StringInfo
+ *secret;
+
+ unsigned char
+ command,
+ session[2*MaxTextExtent];
+
+ /*
+ Distributed pixel cache client.
+ */
+ shared_secret=GetPolicyValue("shared-secret");
+ if (shared_secret == (const char *) NULL)
+ ThrowFatalException(CacheFatalError,"shared secret expected");
+ p=session;
+ (void) CopyMagickString((char *) p,shared_secret,MaxTextExtent);
+ p+=strlen(shared_secret);
+ random_info=AcquireRandomInfo();
+ secret=GetRandomKey(random_info,DPCSessionKeyLength);
+ (void) memcpy(p,GetStringInfoDatum(secret),DPCSessionKeyLength);
+ session_key=GetMagickSignature(secret);
+ random_info=DestroyRandomInfo(random_info);
+ exception=AcquireExceptionInfo();
+ registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
+ (void *(*)(void *)) NULL,RelinquishImageRegistry);
+ client_socket=(*(int *) socket);
+ count=dpc_send(client_socket,DPCSessionKeyLength,GetStringInfoDatum(secret));
+ secret=DestroyStringInfo(secret);
for ( ; ; )
{
- length=(socklen_t) sizeof(address);
- cache_client=accept(cache_socket,(struct sockaddr *) &address,&length);
- (void) fprintf(stdout,"Connection from (%s, %d)\n",
- inet_ntoa(address.sin_addr),(int) ntohs(address.sin_port));
- count=recv(cache_client,buffer,1,0);
- buffer[count]='\0';
- switch (*buffer)
+ count=dpc_read(client_socket,1,(unsigned char *) &command);
+ if (count <= 0)
+ break;
+ count=dpc_read(client_socket,sizeof(key),(unsigned char *) &key);
+ if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
+ break;
+ status=MagickFalse;
+ switch (command)
{
- case 'c':
+ case 'o':
{
- /*
- Create cache.
- */
+ status=OpenDistributeCache(registry,client_socket,session_key,
+ exception);
+ count=dpc_send(client_socket,sizeof(status),(unsigned char *) &status);
break;
}
case 'r':
{
- /*
- Read cache.
- */
+ status=ReadDistributeCachePixels(registry,client_socket,session_key,
+ exception);
+ break;
+ }
+ case 'R':
+ {
+ status=ReadDistributeCacheMetacontent(registry,client_socket,
+ session_key,exception);
+ break;
+ }
+ case 'w':
+ {
+ status=WriteDistributeCachePixels(registry,client_socket,session_key,
+ exception);
break;
}
- case 'u':
+ case 'W':
{
- /*
- Update cache.
- */
+ status=WriteDistributeCacheMetacontent(registry,client_socket,
+ session_key,exception);
break;
}
case 'd':
{
- /*
- Delete cache.
- */
+ status=DestroyDistributeCache(registry,client_socket,session_key);
break;
}
+ default:
+ break;
}
+ if (status == MagickFalse)
+ break;
+ if (command == 'd')
+ break;
+ }
+ count=dpc_send(client_socket,sizeof(status),(unsigned char *) &status);
+ (void) close(client_socket);
+ exception=DestroyExceptionInfo(exception);
+ registry=DestroySplayTree(registry);
+ return((void *) NULL);
+}
+
+MagickExport void DistributePixelCacheServer(const int port,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
+ char
+ service[MaxTextExtent];
+
+ int
+ server_socket,
+ status;
+
+ pthread_attr_t
+ attributes;
+
+ pthread_t
+ threads;
+
+ register struct addrinfo
+ *p;
+
+ struct addrinfo
+ hint,
+ *result;
+
+ struct sockaddr_in
+ address;
+
+ /*
+ Launch distributed pixel cache server.
+ */
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ (void) ResetMagickMemory(&hint,0,sizeof(hint));
+ hint.ai_family=AF_INET;
+ hint.ai_socktype=SOCK_STREAM;
+ hint.ai_flags=AI_PASSIVE;
+ (void) FormatLocaleString(service,MaxTextExtent,"%d",port);
+ status=getaddrinfo((const char *) NULL,service,&hint,&result);
+ if (status != 0)
+ ThrowFatalException(CacheFatalError,"UnableToListen");
+ server_socket=0;
+ for (p=result; p != (struct addrinfo *) NULL; p=p->ai_next)
+ {
+ int
+ one;
+
+ server_socket=socket(p->ai_family,p->ai_socktype,p->ai_protocol);
+ if (server_socket == -1)
+ continue;
+ one=1;
+ status=setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,&one,(socklen_t)
+ sizeof(one));
+ if (status == -1)
+ {
+ (void) close(server_socket);
+ continue;
+ }
+ status=bind(server_socket,p->ai_addr,p->ai_addrlen);
+ if (status == -1)
+ {
+ (void) close(server_socket);
+ continue;
+ }
+ break;
}
+ if (p == (struct addrinfo *) NULL)
+ ThrowFatalException(CacheFatalError,"UnableToBind");
+ freeaddrinfo(result);
+ status=listen(server_socket,DPCPendingConnections);
+ if (status != 0)
+ ThrowFatalException(CacheFatalError,"UnableToListen");
+ pthread_attr_init(&attributes);
+ for ( ; ; )
+ {
+ int
+ client_socket;
+
+ socklen_t
+ length;
+
+ length=(socklen_t) sizeof(address);
+ client_socket=accept(server_socket,(struct sockaddr *) &address,&length);
+ if (client_socket == -1)
+ ThrowFatalException(CacheFatalError,"UnableToEstablishConnection");
+ status=pthread_create(&threads,&attributes,DistributePixelCacheClient,
+ (void *) &client_socket);
+ if (status == -1)
+ ThrowFatalException(CacheFatalError,"UnableToCreateClientThread");
+ }
+ (void) close(server_socket);
#else
- (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
- "DelegateLibrarySupportNotBuiltIn","'%s' (socket)",image_info->filename);
+ ThrowFatalException(MissingDelegateError,"distributed pixel cache");
#endif
}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t D i s t r i b u t e C a c h e F i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDistributeCacheFile() returns the file associated with this
+% DistributeCacheInfo structure.
+%
+% The format of the GetDistributeCacheFile method is:
+%
+% int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+*/
+MagickPrivate int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
+{
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ return(server_info->file);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDistributeCacheHostname() returns the hostname associated with this
+% DistributeCacheInfo structure.
+%
+% The format of the GetDistributeCacheHostname method is:
+%
+% const char *GetDistributeCacheHostname(
+% const DistributeCacheInfo *server_info)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+*/
+MagickPrivate const char *GetDistributeCacheHostname(
+ const DistributeCacheInfo *server_info)
+{
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ return(server_info->hostname);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t D i s t r i b u t e C a c h e P o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDistributeCachePort() returns the port associated with this
+% DistributeCacheInfo structure.
+%
+% The format of the GetDistributeCachePort method is:
+%
+% int GetDistributeCachePort(const DistributeCacheInfo *server_info)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+*/
+MagickPrivate int GetDistributeCachePort(const DistributeCacheInfo *server_info)
+{
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ return(server_info->port);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ O p e n D i s t r i b u t e P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpenDistributePixelCache() opens a pixel cache on a remote server.
+%
+% The format of the OpenDistributePixelCache method is:
+%
+% MagickBooleanType *OpenDistributePixelCache(
+% DistributeCacheInfo *server_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+% o image: the image.
+%
+*/
+MagickPrivate MagickBooleanType OpenDistributePixelCache(
+ DistributeCacheInfo *server_info,Image *image)
+{
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ count;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Open distributed pixel cache.
+ */
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ p=message;
+ *p++='o'; /* open */
+ /*
+ Serialize image attributes (see ValidatePixelCacheMorphology()).
+ */
+ (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
+ p+=sizeof(server_info->session_key);
+ (void) memcpy(p,&image->storage_class,sizeof(image->storage_class));
+ p+=sizeof(image->storage_class);
+ (void) memcpy(p,&image->colorspace,sizeof(image->colorspace));
+ p+=sizeof(image->colorspace);
+ (void) memcpy(p,&image->alpha_trait,sizeof(image->alpha_trait));
+ p+=sizeof(image->alpha_trait);
+ (void) memcpy(p,&image->read_mask,sizeof(image->read_mask));
+ p+=sizeof(image->read_mask);
+ (void) memcpy(p,&image->write_mask,sizeof(image->write_mask));
+ p+=sizeof(image->write_mask);
+ (void) memcpy(p,&image->columns,sizeof(image->columns));
+ p+=sizeof(image->columns);
+ (void) memcpy(p,&image->rows,sizeof(image->rows));
+ p+=sizeof(image->rows);
+ (void) memcpy(p,&image->number_channels,sizeof(image->number_channels));
+ p+=sizeof(image->number_channels);
+ (void) memcpy(p,image->channel_map,MaxPixelChannels*
+ sizeof(*image->channel_map));
+ p+=MaxPixelChannels*sizeof(*image->channel_map);
+ (void) memcpy(p,&image->metacontent_extent,sizeof(image->metacontent_extent));
+ p+=sizeof(image->metacontent_extent);
+ count=dpc_send(server_info->file,p-message,message);
+ if (count != (MagickOffsetType) (p-message))
+ return(MagickFalse);
+ status=MagickFalse;
+ count=dpc_read(server_info->file,sizeof(status),(unsigned char *) &status);
+ if (count != (MagickOffsetType) sizeof(status))
+ return(MagickFalse);
+ return(status);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadDistributePixelCacheMetacontents() reads metacontent from the specified
+% region of the distributed pixel cache.
+%
+% The format of the ReadDistributePixelCacheMetacontents method is:
+%
+% MagickOffsetType ReadDistributePixelCacheMetacontents(
+% DistributeCacheInfo *server_info,const RectangleInfo *region,
+% const MagickSizeType length,unsigned char *metacontent)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+% o image: the image.
+%
+% o region: read the metacontent from this region of the image.
+%
+% o length: the length in bytes of the metacontent.
+%
+% o metacontent: read these metacontent from the pixel cache.
+%
+*/
+MagickPrivate MagickOffsetType ReadDistributePixelCacheMetacontent(
+ DistributeCacheInfo *server_info,const RectangleInfo *region,
+ const MagickSizeType length,unsigned char *metacontent)
+{
+ MagickOffsetType
+ count;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Read distributed pixel cache metacontent.
+ */
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ assert(region != (RectangleInfo *) NULL);
+ assert(metacontent != (unsigned char *) NULL);
+ if (length > (MagickSizeType) SSIZE_MAX)
+ return(-1);
+ p=message;
+ *p++='R';
+ (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
+ p+=sizeof(server_info->session_key);
+ (void) memcpy(p,®ion->width,sizeof(region->width));
+ p+=sizeof(region->width);
+ (void) memcpy(p,®ion->height,sizeof(region->height));
+ p+=sizeof(region->height);
+ (void) memcpy(p,®ion->x,sizeof(region->x));
+ p+=sizeof(region->x);
+ (void) memcpy(p,®ion->y,sizeof(region->y));
+ p+=sizeof(region->y);
+ (void) memcpy(p,&length,sizeof(length));
+ p+=sizeof(length);
+ count=dpc_send(server_info->file,p-message,message);
+ if (count != (MagickOffsetType) (p-message))
+ return(-1);
+ return(dpc_read(server_info->file,length,metacontent));
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadDistributePixelCachePixels() reads pixels from the specified region of
+% the distributed pixel cache.
+%
+% The format of the ReadDistributePixelCachePixels method is:
+%
+% MagickOffsetType ReadDistributePixelCachePixels(
+% DistributeCacheInfo *server_info,const RectangleInfo *region,
+% const MagickSizeType length,unsigned char *restrict pixels)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+% o image: the image.
+%
+% o region: read the pixels from this region of the image.
+%
+% o length: the length in bytes of the pixels.
+%
+% o pixels: read these pixels from the pixel cache.
+%
+*/
+MagickPrivate MagickOffsetType ReadDistributePixelCachePixels(
+ DistributeCacheInfo *server_info,const RectangleInfo *region,
+ const MagickSizeType length,unsigned char *restrict pixels)
+{
+ MagickOffsetType
+ count;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Read distributed pixel cache pixels.
+ */
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ assert(region != (RectangleInfo *) NULL);
+ assert(pixels != (unsigned char *) NULL);
+ if (length > (MagickSizeType) SSIZE_MAX)
+ return(-1);
+ p=message;
+ *p++='r';
+ (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
+ p+=sizeof(server_info->session_key);
+ (void) memcpy(p,®ion->width,sizeof(region->width));
+ p+=sizeof(region->width);
+ (void) memcpy(p,®ion->height,sizeof(region->height));
+ p+=sizeof(region->height);
+ (void) memcpy(p,®ion->x,sizeof(region->x));
+ p+=sizeof(region->x);
+ (void) memcpy(p,®ion->y,sizeof(region->y));
+ p+=sizeof(region->y);
+ (void) memcpy(p,&length,sizeof(length));
+ p+=sizeof(length);
+ count=dpc_send(server_info->file,p-message,message);
+ if (count != (MagickOffsetType) (p-message))
+ return(-1);
+ return(dpc_read(server_info->file,length,pixels));
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RelinquishDistributePixelCache() frees resources acquired with
+% OpenDistributePixelCache().
+%
+% The format of the RelinquishDistributePixelCache method is:
+%
+% MagickBooleanType RelinquishDistributePixelCache(
+% DistributeCacheInfo *server_info)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+*/
+MagickPrivate MagickBooleanType RelinquishDistributePixelCache(
+ DistributeCacheInfo *server_info)
+{
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ count;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Delete distributed pixel cache.
+ */
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ p=message;
+ *p++='d';
+ (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
+ p+=sizeof(server_info->session_key);
+ count=dpc_send(server_info->file,p-message,message);
+ if (count != (MagickOffsetType) (p-message))
+ return(MagickFalse);
+ count=dpc_read(server_info->file,sizeof(status),(unsigned char *) &status);
+ if (count != (MagickOffsetType) sizeof(status))
+ return(MagickFalse);
+ return(status);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteDistributePixelCacheMetacontents() writes image metacontent to the
+% specified region of the distributed pixel cache.
+%
+% The format of the WriteDistributePixelCacheMetacontents method is:
+%
+% MagickOffsetType WriteDistributePixelCacheMetacontents(
+% DistributeCacheInfo *server_info,const RectangleInfo *region,
+% const MagickSizeType length,const unsigned char *metacontent)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+% o image: the image.
+%
+% o region: write the metacontent to this region of the image.
+%
+% o length: the length in bytes of the metacontent.
+%
+% o metacontent: write these metacontent to the pixel cache.
+%
+*/
+MagickPrivate MagickOffsetType WriteDistributePixelCacheMetacontent(
+ DistributeCacheInfo *server_info,const RectangleInfo *region,
+ const MagickSizeType length,const unsigned char *metacontent)
+{
+ MagickOffsetType
+ count;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Write distributed pixel cache metacontent.
+ */
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ assert(region != (RectangleInfo *) NULL);
+ assert(metacontent != (unsigned char *) NULL);
+ if (length > (MagickSizeType) SSIZE_MAX)
+ return(-1);
+ p=message;
+ *p++='W';
+ (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
+ p+=sizeof(server_info->session_key);
+ (void) memcpy(p,®ion->width,sizeof(region->width));
+ p+=sizeof(region->width);
+ (void) memcpy(p,®ion->height,sizeof(region->height));
+ p+=sizeof(region->height);
+ (void) memcpy(p,®ion->x,sizeof(region->x));
+ p+=sizeof(region->x);
+ (void) memcpy(p,®ion->y,sizeof(region->y));
+ p+=sizeof(region->y);
+ (void) memcpy(p,&length,sizeof(length));
+ p+=sizeof(length);
+ count=dpc_send(server_info->file,p-message,message);
+ if (count != (MagickOffsetType) (p-message))
+ return(-1);
+ return(dpc_send(server_info->file,length,metacontent));
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteDistributePixelCachePixels() writes image pixels to the specified
+% region of the distributed pixel cache.
+%
+% The format of the WriteDistributePixelCachePixels method is:
+%
+% MagickBooleanType WriteDistributePixelCachePixels(
+% DistributeCacheInfo *server_info,const RectangleInfo *region,
+% const MagickSizeType length,const unsigned char *restrict pixels)
+%
+% A description of each parameter follows:
+%
+% o server_info: the distributed cache info.
+%
+% o image: the image.
+%
+% o region: write the pixels to this region of the image.
+%
+% o length: the length in bytes of the pixels.
+%
+% o pixels: write these pixels to the pixel cache.
+%
+*/
+MagickPrivate MagickOffsetType WriteDistributePixelCachePixels(
+ DistributeCacheInfo *server_info,const RectangleInfo *region,
+ const MagickSizeType length,const unsigned char *restrict pixels)
+{
+ MagickOffsetType
+ count;
+
+ register unsigned char
+ *p;
+
+ unsigned char
+ message[MaxTextExtent];
+
+ /*
+ Write distributed pixel cache pixels.
+ */
+ assert(server_info != (DistributeCacheInfo *) NULL);
+ assert(server_info->signature == MagickSignature);
+ assert(region != (RectangleInfo *) NULL);
+ assert(pixels != (const unsigned char *) NULL);
+ if (length > (MagickSizeType) SSIZE_MAX)
+ return(-1);
+ p=message;
+ *p++='w';
+ (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
+ p+=sizeof(server_info->session_key);
+ (void) memcpy(p,®ion->width,sizeof(region->width));
+ p+=sizeof(region->width);
+ (void) memcpy(p,®ion->height,sizeof(region->height));
+ p+=sizeof(region->height);
+ (void) memcpy(p,®ion->x,sizeof(region->x));
+ p+=sizeof(region->x);
+ (void) memcpy(p,®ion->y,sizeof(region->y));
+ p+=sizeof(region->y);
+ (void) memcpy(p,&length,sizeof(length));
+ p+=sizeof(length);
+ count=dpc_send(server_info->file,p-message,message);
+ if (count != (MagickOffsetType) (p-message))
+ return(-1);
+ return(dpc_send(server_info->file,length,pixels));
+}