File: Unix.sml

package info (click to toggle)
polyml 5.7.1-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 40,616 kB
  • sloc: cpp: 44,142; ansic: 26,963; sh: 22,002; asm: 13,486; makefile: 602; exp: 525; python: 253; awk: 91
file content (220 lines) | stat: -rw-r--r-- 8,847 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
(*
    Title:      Standard Basis Library: Unix structure and signature.
    Author:     David Matthews
    Copyright   David Matthews 2000,2008

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*)

signature UNIX =
sig
    type ('a,'b) proc
    type signal
    datatype exit_status
       = W_EXITED
       | W_EXITSTATUS of Word8.word
       | W_SIGNALED (* sic *) of signal
       | W_STOPPED of signal
    val fromStatus : OS.Process.status -> exit_status
    val executeInEnv : string * string list * string list -> ('a, 'b) proc
    val execute : string * string list -> ('a, 'b) proc
    val textInstreamOf : (TextIO.instream, 'a) proc  -> TextIO.instream
    val binInstreamOf  : (BinIO.instream, 'a) proc -> BinIO.instream
    val textOutstreamOf : ('a, TextIO.outstream) proc -> TextIO.outstream
    val binOutstreamOf  : ('a, BinIO.outstream) proc -> BinIO.outstream
    val streamsOf : (TextIO.instream, TextIO.outstream) proc
                       -> TextIO.instream * TextIO.outstream
    val reap : ('a, 'b) proc -> OS.Process.status
    val kill : ('a, 'b) proc * signal -> unit
    val exit : Word8.word -> 'a
end;

structure Unix :>
    sig
        (* We have to copy the signature since we can't establish the
           connection between exit_status and Posix.Process.exit_status
           with a "where type". *)
        type ('a,'b) proc
        type signal = Posix.Signal.signal
        datatype exit_status = datatype Posix.Process.exit_status
        val fromStatus : OS.Process.status -> exit_status
        val executeInEnv : string * string list * string list -> ('a, 'b) proc
        val execute : string * string list -> ('a, 'b) proc
        val textInstreamOf : (TextIO.instream, 'a) proc  -> TextIO.instream
        val binInstreamOf  : (BinIO.instream, 'a) proc -> BinIO.instream
        val textOutstreamOf : ('a, TextIO.outstream) proc -> TextIO.outstream
        val binOutstreamOf  : ('a, BinIO.outstream) proc -> BinIO.outstream
        val streamsOf : (TextIO.instream, TextIO.outstream) proc
                           -> TextIO.instream * TextIO.outstream
        val reap : ('a, 'b) proc -> OS.Process.status
        val kill : ('a, 'b) proc * signal -> unit
        val exit : Word8.word -> 'a
    end = 
struct
    type ('a,'b) proc =
     { pid: Posix.Process.pid,
       infd: Posix.IO.file_desc,
       outfd: Posix.IO.file_desc,
       (* We have to remember the result status. *)
       result: OS.Process.status option ref
     }
    type signal = Posix.Signal.signal
    datatype exit_status = datatype Posix.Process.exit_status

    val fromStatus = Posix.Process.fromStatus

    fun kill({pid, ... }: ('a, 'b) proc, signal) =
        Posix.Process.kill(Posix.Process.K_PROC pid, signal)

    (* Create a new process running a command and with pipes connecting the
       standard input and output.
       The command is supposed to be an executable and we should raise an
       exception if it is not.  Since the exece is only done in the child we
       need to test whether we have an executable at the beginning.
       The definition does not say whether the first of the user-supplied
       arguments includes the command or not.  Assume that only the "real"
       arguments are provided and pass the last component of the command
       name in the exece call. *)
    fun executeInEnv (cmd, args, env) =
    let
        open Posix
        (* Test first for presence of the file and then that we
           have correct access rights. *)
        val s = FileSys.stat cmd (* Raises SysErr if the file doesn't exist. *)
        val () =
           if not (FileSys.ST.isReg s) orelse not (FileSys.access(cmd, [FileSys.A_EXEC]))
           then raise OS.SysErr(OS.errorMsg Error.acces, SOME Error.acces)
           else ()
        val toChild = IO.pipe()
        and fromChild = IO.pipe()
    in
        case Process.fork() of
            NONE => (* In the child *)
            ((
            (* Should really clean up the signals here and
               turn off timers. *)
            (* Close the unwanted ends of the pipes and
               set the required ends up as stdin and stdout. *)
            IO.close(#outfd toChild);
            IO.close(#infd fromChild);
            IO.dup2{old= #infd toChild,
                      new=FileSys.wordToFD 0w0};
            IO.dup2{old= #outfd fromChild,
                      new= FileSys.wordToFD 0w1};
            IO.close(#infd toChild);
            IO.close(#outfd fromChild);
            (* Run the command. *)
            Process.exece(cmd, OS.Path.file cmd :: args, env);
            (* If we get here the exec must have failed -
               terminate this process.  We're supposed to
               set the error code to 126 in this case. *)
            Process.exit 0w126
            ) handle _ => Process.exit 0w126)

        |   SOME pid => (* In the parent *)
            (
            IO.close(#infd toChild);
            IO.close(#outfd fromChild);
            {pid=pid, infd= #infd fromChild, outfd= #outfd toChild, result = ref NONE}
            )
    end

    fun execute (cmd, args) =
        executeInEnv(cmd, args, Posix.ProcEnv.environ())

    local (* Internal function to get the preferred buffer size. *)
        val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
    in
        fun sys_get_buffsize (strm: OS.IO.iodesc): int = doIo(15, strm, 0)
    end

    fun textInstreamOf {infd, ...} =
    let
        val n = Posix.FileSys.fdToIOD infd
        val textPrimRd =
            LibraryIOSupport.wrapInFileDescr
                {fd=n, name="TextPipeInput", initBlkMode=true}
        val streamIo = TextIO.StreamIO.mkInstream(textPrimRd, "")
    in
        TextIO.mkInstream streamIo
    end
        
    fun textOutstreamOf {outfd, ...} =
    let
        val n = Posix.FileSys.fdToIOD outfd
        val buffSize = sys_get_buffsize n
        val textPrimWr =
            LibraryIOSupport.wrapOutFileDescr{fd=n, name="TextPipeOutput",
                appendMode=false, initBlkMode=true, chunkSize=buffSize}
        (* Construct a stream. *)
        val streamIo = TextIO.StreamIO.mkOutstream(textPrimWr, IO.LINE_BUF)
    in
        TextIO.mkOutstream streamIo
    end

    fun binInstreamOf {infd, ...} =
    let
        val n = Posix.FileSys.fdToIOD infd
        val binPrimRd =
            LibraryIOSupport.wrapBinInFileDescr{fd=n, name="BinPipeInput", initBlkMode=true}
        val streamIo =
            BinIO.StreamIO.mkInstream(binPrimRd, Word8Vector.fromList [])
    in
        BinIO.mkInstream streamIo
    end
        
    fun binOutstreamOf {outfd, ...} =
    let
        val n = Posix.FileSys.fdToIOD outfd
        val buffSize = sys_get_buffsize n
        val binPrimWr =
            LibraryIOSupport.wrapBinOutFileDescr{fd=n, name="BinPipeOutput",
                appendMode=false, chunkSize=buffSize, initBlkMode=true}
        (* Construct a stream. *)
        val streamIo = BinIO.StreamIO.mkOutstream(binPrimWr, IO.LINE_BUF)
    in
        BinIO.mkOutstream streamIo
    end

    fun streamsOf p = (textInstreamOf p, textOutstreamOf p)

    (* Internal function - inverse of Posix.Process.fromStatus. *)
    local
        val doCall = RunCall.rtsCallFull2 "PolyOSSpecificGeneral"
    in
        fun toStatus W_EXITED: OS.Process.status = doCall(16, (1, 0))
         |  toStatus(W_EXITSTATUS w) = doCall(16, (1, Word8.toInt w))
         |  toStatus(W_SIGNALED s) =
            doCall(16, (2, SysWord.toInt(Posix.Signal.toWord s)))
         |  toStatus(W_STOPPED s) = 
            doCall(16, (3, SysWord.toInt(Posix.Signal.toWord s)))
    end

    fun reap {result = ref(SOME r), ...} = r
    |   reap(p as {pid, infd, outfd, result}) =
    let
        val () = Posix.IO.close infd;
        val () = Posix.IO.close outfd;
        val (_, status) =
            Posix.Process.waitpid(Posix.Process.W_CHILD pid, [])
    in
        (* If the process is only stopped we need to wait again. *)
        case status of
            W_STOPPED _ => reap p
        |   _ => let val s = toStatus status in result := SOME s; s end
    end

    fun exit w = OS.Process.exit(toStatus (W_EXITSTATUS w))
end;