forked from cockpit-project/cockpit-project.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcockpit-file.html
More file actions
265 lines (256 loc) · 13 KB
/
cockpit-file.html
File metadata and controls
265 lines (256 loc) · 13 KB
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
---
layout: guide
---
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>cockpit.js: File Access</title>
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
<link rel="home" href="index.html" title="Cockpit Guide">
<link rel="up" href="api-base1.html" title="API: base1">
<link rel="prev" href="cockpit-dbus.html" title="cockpit.js: DBus Client">
<link rel="next" href="cockpit-http.html" title="cockpit.js: HTTP Client">
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<table class="navigation" id="top" width="100%" summary="Navigation header" cellpadding="2" cellspacing="2"><tr valign="middle">
<td><a accesskey="p" href="cockpit-dbus.html"><img src="left.png" width="24" height="24" border="0" alt="Prev"></a></td>
<td><a accesskey="u" href="api-base1.html"><img src="up.png" width="24" height="24" border="0" alt="Up"></a></td>
<td><a accesskey="h" href="index.html"><img src="home.png" width="24" height="24" border="0" alt="Home"></a></td>
<th width="100%" align="center">Cockpit Guide</th>
<td><a accesskey="n" href="cockpit-http.html"><img src="right.png" width="24" height="24" border="0" alt="Next"></a></td>
</tr></table>
<div class="refentry">
<a name="cockpit-file"></a><div class="titlepage"></div>
<div class="refnamediv"><table width="100%"><tr>
<td valign="top">
<h2>cockpit.js: File Access</h2>
<p>cockpit.js: File Access — Reading, writing, and watching files.</p>
</td>
<td valign="top" align="right"></td>
</tr></table></div>
<div class="refsynopsisdiv">
<h2>Synopsis</h2>
<p>The <code class="code">cockpit.file</code> API lets you read, write, and watch regular files in their entirety.
It cannot efficiently do random access in a big file or read non-regular files such as
<code class="code">/dev/random</code>.
</p>
<pre class="programlisting">
file = cockpit.file(path,
{ syntax: syntax_object,
binary: boolean,
max_read_size: int,
superuser: string,
})
promise = file.read()
promise
.then((content, tag) => { ... })
.catch(error => { ... })
promise = file.replace(content, [ expected_tag ])
promise
.then(new_tag => { ... })
.catch(error => { ... })
promise = file.modify(callback, [ initial_content, initial_tag ]
promise
.then((new_content, new_tag) => { ... })
.catch(error => { ... })
file.watch((content, tag, [error]) => { }, [ { read: boolean } ])
file.close()
</pre>
</div>
<div class="refsection">
<a name="cockpit-file-simple"></a><h2>Simple reading and writing</h2>
<p>You can read a file with code like this:</p>
<pre class="programlisting">
cockpit.file("/path/to/file").read()
.then((content, tag) => {
...
})
.catch(error => {
...
});
</pre>
<p>It is recommended to use absolute paths. Relative paths are resolved against <code class="code">/</code>.
To work with the current user's files <a class="link" href="cockpit-login.html#cockpit-user" title="cockpit.user()">cockpit.user()</a> can be used to get the user's home directory.
</p>
<p>The <code class="code">read()</code> method returns a
<a class="ulink" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_top">Promise</a>.</p>
<p>When successful, the promise will be resolved with the content of the
file. Unless you specify options to change this (see below), the file
is assumed to be text in the UTF-8 encoding, and <code class="code">content</code>
will be a string.</p>
<p>The tag that is passed to the <code class="code">then()</code> callback is a short
string that is associated with the file and changes whenever the
content of the file changes. It is meant to be used with <code class="code">replace()</code>.</p>
<p>It is not an error when the file does not exist. In this case, the
<code class="code">then()</code> callback will be called with a <code class="code">null</code>
value for <code class="code">content</code> and <code class="code">tag</code> is <code class="code">"-"</code>.</p>
<p>The <code class="code">superuser</code> option can be used the same way
as described in the <a class="link" href="cockpit-channels.html#cockpit-channels-channel" title="cockpit.channel()">cockpit.channel()</a>
to provide a different access level to the file.</p>
<p>You can use the <code class="code">max_read_size</code> option to limit
the amount of data that is read. If the file is larger than the
given number of bytes, no data is read and the channel is closed with
problem code <code class="code">too-large</code>. The default limit is 16 MiB. The
limit can be completely removed by setting it to -1.</p>
<p>To write to a file, use code like this:
</p>
<pre class="programlisting">
cockpit.file("/path/to/file").replace("my new content\n")
.then(tag => {
...
})
.catch(error => {
...
});
</pre>
<p>
</p>
<p>The <code class="code">replace()</code> method returns a
<a class="ulink" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_top">Promise</a>.</p>
<p>When the promise is resolved, the file has been atomically replaced
(via the <code class="code">rename()</code> syscall) with the new content. As with
<code class="code">read()</code>, by default the new content is a string and will
be written to the file as UTF-8. The returned tag corresponds to the
new content of the file.</p>
<p>When the promise is rejected because of an error, the file or its meta
data has not been changed in any way.</p>
<p>As a special case, passing the value <code class="code">null</code> to
<code class="code">replace()</code> will remove the file.</p>
<p>The <code class="code">replace()</code> method can also check for conflicting
changes to a file. You can pass a tag (as returned by
<code class="code">read()</code> or <code class="code">replace()</code>) to
<code class="code">replace()</code>, and the file will only be replaced if it still
has the given tag. If the tag of the file has changed,
<code class="code">replace()</code> will fail with an error object that has
<code class="code">error.problem == "change-conflict"</code>. See
<code class="code">modify()</code> below for a convenient way to achieve
transactional updates to a file.</p>
</div>
<div class="refsection">
<a name="cockpit-file-format"></a><h2>File format</h2>
<p>By default, a file is assumed to be text encoded in UTF-8, and the
<code class="code">read()</code> and <code class="code">replace()</code> functions use strings to
represent the content.</p>
<p>By specifying the <code class="code">syntax.parser()</code> and
<code class="code">syntax.stringify()</code> options, you can cause
<code class="code">read()</code> to parse the content before passing it back to
you, and <code class="code">replace()</code> to unparse it before writing.</p>
<p>The main idea is to be able to write <code class="code">{ syntax: JSON }</code>, of
course, but you can easily pass in individual functions or make your
own parser/unparser object:
</p>
<pre class="programlisting">
cockpit.file("/path/to/file.json", { syntax: JSON })
var syntax_object = {
parse: my_parser,
stringify: my_unparser
};
cockpit.file("/path/to/file", { syntax: syntax_object })
</pre>
<p>
Any exceptions thrown by the <code class="code">parse()</code> and
<code class="code">stringify()</code> functions are caught and reported as read or
write errors.</p>
<p>The <code class="code">null</code> value that is used to represent the content of a
non-existing file (see "Simple reading and writing", above) is not
passed through the <code class="code">parse()</code> and <code class="code">stringify()</code>
functions.</p>
</div>
<div class="refsection">
<a name="cockpit-file-binary"></a><h2>Binary files</h2>
<p>By default the content of the file is assumed to be text encoded as
UTF-8 and it can not contain zero bytes. The content is represented
as a JavaScript string with <code class="code">read()</code>,
<code class="code">replace()</code>, etc. By setting the <code class="code">binary</code> option
to true when creating the proxy, no assumptions are placed on the
content, and it is represented as a <code class="code">Uint8Array</code> in
JavaScript.</p>
</div>
<div class="refsection">
<a name="cockpit-file-atomic"></a><h2>Atomic modifications</h2>
<p>Use <code class="code">modify()</code> to modify the content of the file safely. A
call to <code class="code">modify()</code> will read the content of the file, call
<code class="code">callback</code> on the content, and then replace the content of
the file with the return value of the callback.</p>
<p>The <code class="code">modify()</code> method uses the <code class="code">read()</code> and
<code class="code">replace()</code> methods internally in the obvious way. Thus,
the <code class="code">syntax.parse()</code> and <code class="code">syntax.stringify()</code>
options work as expected, <code class="code">null</code> represents a non-existing
file, and the watch callbacks are fired.</p>
<p>It will do this one or more times, until no other conflicting changes
have been made to the file between reading and replacing it.</p>
<p>The callback is called like this
</p>
<pre class="programlisting">
new_content = callback (old_content)
</pre>
<p>
The callback is allowed to mutate <code class="code">old_content</code>, but note
that this will also mutate the objects that are passed to the watch
callbacks. Returning <code class="code">undefined</code> from the proxy is the
same as returning <code class="code">old_content</code>.</p>
<p>The <code class="code">modify()</code> method returns a
<a class="ulink" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_top">Promise</a>.</p>
<p>The promise will be resolved with the new content and its tag, like so
</p>
<pre class="programlisting">
function shout(old_content) {
return old_content.toUpperCase();
}
cockpit.file("/path/to/file").modify(shout)
.then((content, tag) => {
...
})
.catch(error => {
...
});
</pre>
<p>
If you have cached the last content and tag results of the
<code class="code">read()</code> or <code class="code">modify()</code> method, or the last
values passed to a watch callback, you can pass them to
<code class="code">modify()</code> as the second and third argument. In this case,
<code class="code">modify()</code> will skip the initial read and start with the
given values.</p>
</div>
<div class="refsection">
<a name="cockpit-file-notify"></a><h2>Change notifications</h2>
<p>Calling <code class="code">watch()</code> will start monitoring the file for
external changes.
</p>
<pre class="programlisting">
handle = file.watch(callback);
handle_no_read = file.watch(callback, { read: false });
</pre>
<p>
Whenever a change occurs, the <code class="code">callback()</code> is called with
the new content and tag of the file. This might happen because of
external changes, but also as part of calls to <code class="code">read()</code>,
<code class="code">replace()</code>, and <code class="code">modify()</code>.</p>
<p>When a read error occurs, the <code class="code">callback()</code> is called with
an error as a third argument. Write errors are not reported via the watch callback.</p>
<p>Calling <code class="code">watch()</code> will also automatically call <code class="code">read()</code>
to get the initial content of the file. Thus, you normally don't need to call
<code class="code">read()</code> at all when using <code class="code">watch()</code>.</p>
<p>To disable the automatic reading, e.g. for large files or unreadable
file system objects, set the <code class="code">read</code> option to <code class="code">false</code>. The first
<code class="code">content</code> argument of the callback will then always be <code class="code">null</code>.</p>
<p>To free the resources used for monitoring, call <code class="code">handle.remove()</code>.</p>
</div>
<div class="refsection">
<a name="cockpit-file-path"></a><h2>file.path</h2>
<p>A string containing the path that was passed to the <code class="code">cockpit.file()</code>
method.</p>
</div>
<div class="refsection">
<a name="cockpit-file-close"></a><h2>Closing</h2>
<p>Call the <code class="code">close()</code> method on a file proxy to cancel all
ongoing operations, such as reading, writing, and monitoring. The
proxy should not be used after closing it.</p>
</div>
</div>
<div class="footer"><hr></div>
</body>
</html>