1 /*******************************************************************************
2   copyright:   Copyright (c) 2006 Juan Jose Comellas. All rights reserved
3   license:     BSD style: $(LICENSE)
4   author:      Juan Jose Comellas <juanjo@comellas.com.ar>
5 *******************************************************************************/
6 
7 module tango.sys.Pipe;
8 
9 private import tango.sys.Common;
10 private import tango.io.device.Device;
11 
12 private import tango.core.Exception;
13 
14 version (Posix)
15 {
16     private import tango.stdc.posix.unistd;
17 }
18 
19 debug (PipeConduit)
20 {
21     private import tango.io.Stdout;
22 }
23 
24 private enum {DefaultBufferSize = 8 * 1024}
25 
26 
27 /**
28  * Conduit for pipes.
29  *
30  * Each PipeConduit can only read or write, depending on the way it has been
31  * created.
32  */
33 
34 class PipeConduit : Device
35 {
36     version (OLD)
37     {
38         alias Device.fileHandle  fileHandle;
39         alias Device.copy        copy;
40         alias Device.read        read;
41         alias Device.write       write;
42         alias Device.close       close;
43         alias Device.error       error;
44     }
45 
46     private uint _bufferSize;
47 
48 
49     /**
50      * Create a PipeConduit with the provided handle and access permissions.
51      *
52      * Params:
53      * handle       = handle of the operating system pipe we will wrap inside
54      *                the PipeConduit.
55      * style        = access flags for the pipe (readable, writable, etc.).
56      * bufferSize   = buffer size.
57      */
58     private this(Handle handle, uint bufferSize = DefaultBufferSize)
59     {
60         version (Windows)
61                  io.handle = handle;
62             else
63                this.handle = handle;
64         _bufferSize = bufferSize;
65     }
66 
67     /**
68      * Destructor.
69      */
70     public ~this()
71     {
72         close();
73     }
74 
75     /**
76      * Returns the buffer size for the PipeConduit.
77      */
78     public override const size_t bufferSize()
79     {
80         return _bufferSize;
81     }
82 
83     /**
84      * Returns the name of the device.
85      */
86     public override immutable(char)[] toString()
87     {
88         return "<pipe>";
89     }
90 
91     version (OLD)
92     {
93         /**
94          * Read a chunk of bytes from the file into the provided array 
95          * (typically that belonging to an IBuffer)
96          */
97         protected override uint read (void[] dst)
98         {
99             uint result;
100             DWORD read;
101             void *p = dst.ptr;
102 
103             if (!ReadFile (handle, p, dst.length, &read, null))
104             {
105                 if (SysError.lastCode() == ERROR_BROKEN_PIPE)
106                 {
107                     return Eof;
108                 }
109                 else
110                 {
111                     error();
112                 }
113             }
114 
115             if (read == 0 && dst.length > 0)
116             {
117                 return Eof;
118             }
119             return read;
120         }
121 
122         /**
123          * Write a chunk of bytes to the file from the provided array 
124          * (typically that belonging to an IBuffer).
125          */
126         protected override uint write (const(void)[] src)
127         {
128             DWORD written;
129 
130             if (!WriteFile (handle, src.ptr, src.length, &written, null))
131             {
132                 error();
133             }
134             return written;
135         }
136     }
137 }
138 
139 /**
140  * Factory class for Pipes.
141  */
142 class Pipe
143 {
144     private PipeConduit _source;
145     private PipeConduit _sink;
146 
147     /**
148      * Create a Pipe.
149      */
150     public this(uint bufferSize = DefaultBufferSize)
151     {
152         version (Windows)
153         {
154             this(bufferSize, null);
155         }
156         else version (Posix)
157         {
158             int[2] fd;
159 
160             if (pipe(fd.ptr) == 0)
161             {
162                 _source = new PipeConduit(cast(ISelectable.Handle) fd[0], bufferSize);
163                 _sink = new PipeConduit(cast(ISelectable.Handle) fd[1], bufferSize);
164             }
165             else
166             {
167                 error();
168             }
169         }
170         else
171         {
172             assert(false, "Unknown platform");
173         }
174     }
175 
176     version (Windows)
177     {
178         /**
179          * Helper constructor for pipes on Windows with non-null security
180          * attributes.
181          */
182         package this(uint bufferSize, SECURITY_ATTRIBUTES *sa)
183         {
184             HANDLE sinkHandle;
185             HANDLE sourceHandle;
186 
187             if (CreatePipe(&sourceHandle, &sinkHandle, sa, cast(DWORD) bufferSize))
188             {
189                 _source = new PipeConduit(sourceHandle);
190                 _sink = new PipeConduit(sinkHandle);
191             }
192             else
193             {
194                 error();
195             }
196         }
197     }
198 
199     /**
200      * Return the PipeConduit that you can write to.
201      */
202     @property public PipeConduit sink()
203     {
204         return _sink;
205     }
206 
207     /**
208      * Return the PipeConduit that you can read from.
209      */
210     @property public PipeConduit source()
211     {
212         return _source;
213     }
214 
215     /**
216      *
217      */
218     private final void error ()
219     {
220         throw new IOException("Pipe error: " ~ SysError.lastMsg.idup);
221     }
222 }
223