summaryrefslogtreecommitdiff
path: root/doc/special_remotes/external/example.sh
blob: 81b4b6806cece8ba860df5168aa9f111be31a835 (plain)
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#!/bin/sh
# git-annex external special remote program
# 
# This is basically the same as git-annex's built-in directory special remote.
# 
# Install in PATH as git-annex-remote-directory
#
# Copyright 2013 Joey Hess; licenced under the GNU GPL version 3 or higher.

set -e

# This program speaks a line-based protocol on stdin and stdout.
# When running any commands, their stdout should be redirected to stderr
# (or /dev/null) to avoid messing up the protocol.
runcmd () {
	"$@" >&2
}

# Gets a value from the remote's configuration, and stores it in RET
getconfig () {
	ask GETCONFIG "$1"
}

# Stores a value in the remote's configuration.
setconfig () {
	echo SETCONFIG "$1" "$2"
}

# Sets LOC to the location to use to store a key.
calclocation () {
	ask DIRHASH "$1"
	LOC="$mydirectory/$RET/$1"
}

# Asks for some value, and stores it in RET
ask () {
	echo "$1" "$2"
	read resp
	# Tricky POSIX shell code to split first word of the resp,
	# preserving all other whitespace
	case "${resp%% *}" in
		VALUE)
			RET="$(echo "$resp" | sed 's/^VALUE \?//')"
		;;
		*)
			RET=""
		;;
	esac
}

# This remote doesn't need credentials to access it,
# but many of them will. Here's how to handle requiring the user
# set MYPASSWORD and MYLOGIN when running initremote. The creds
# will be stored securely for later use, so the user only needs
# to provide them once.
setupcreds () {
	if [ -z "$MYPASSWORD" ] || [ -z "$MYLOGIN" ]; then
		echo INITREMOTE-FAILURE "You need to set MYPASSWORD and MYLOGIN environment variables when running initremote."
	else
		echo SETCREDS mycreds "$MYLOGIN" "$MYPASSWORD"	
		echo INITREMOTE-SUCCESS
	fi
}

getcreds () {
	echo GETCREDS mycreds
	read resp
	case "${resp%% *}" in
		CREDS)
			MYLOGIN="$(echo "$resp" | sed 's/^CREDS \([^ ]*\) .*/\1/')"
			MYPASSWORD="$(echo "$resp" | sed 's/^CREDS [^ ]* //')"
		;;
	esac

}

dostore () {
	local key="$1"
	local file="$2"
	local loc="$3"
	mkdir -p "$(dirname "$loc")"
	# Store in temp file first, so that CHECKPRESENT does not see it
	# until it is all stored.
	mkdir -p "$mydirectory/tmp"
	tmp="$mydirectory/tmp/$key"
	# XXX when at all possible, send PROGRESS while transferring
	# the file.
	rm -f "$tmp"
	if runcmd cp "$file" "$tmp" \
	   && runcmd mv -f "$tmp" "$loc"; then
		echo TRANSFER-SUCCESS STORE "$key"
	else
		echo TRANSFER-FAILURE STORE "$key"
	fi
	rmdir "$mydirectory/tmp"
}

doretrieve () {
	local key="$1"
	local file="$2"
	local loc="$3"
	
	# XXX when easy to do, send PROGRESS while transferring the file
	if [ -e "$loc" ]; then
		if runcmd cp "$loc" "$file"; then
			echo TRANSFER-SUCCESS RETRIEVE "$key"
		else
			echo TRANSFER-FAILURE RETRIEVE "$key"
		fi
	else
		echo TRANSFER-FAILURE RETRIEVE "$key"
	fi
}

docheckpresent () {
	local key="$1"
	local loc="$2"

	if [ -e "$loc" ]; then
		echo CHECKPRESENT-SUCCESS "$key"
	else
		if [ -d "$mydirectory" ]; then
			echo CHECKPRESENT-FAILURE "$key"
		else
			# When the directory does not exist,
			# the remote is not available.
			# (A network remote would similarly
			# fail with CHECKPRESENT-UNKNOWN
			# if it couldn't be contacted).
			echo CHECKPRESENT-UNKNOWN "$key" "this remote is not currently available"
		fi
	fi
}

doremove () {
	local key="$1"
	local loc="$2"

	# Note that it's not a failure to remove a
	# fike that is not present.
	if [ -e "$loc" ]; then
		if runcmd rm -f "$loc"; then
			echo REMOVE-SUCCESS "$key"
		else
			echo REMOVE-FAILURE "$key"
		fi
	else
		echo REMOVE-SUCCESS "$key"
	fi
}

# This has to come first, to get the protocol started.
echo VERSION 1

while read line; do
	set -- $line
	case "$1" in
		INITREMOTE)
			# Do anything necessary to create resources
			# used by the remote. Try to be idempotent.
			# 
			# Use GETCONFIG to get any needed configuration
			# settings, and SETCONFIG to set any persistent
			# configuration settings.
			# 
			# (Note that this is not run every time, only when
			# git annex initremote or git annex enableremote is
			# run.)

			# The directory provided by the user
			# could be relative; make it absolute,
			# and store that.
			getconfig directory
			mydirectory="$(readlink -f "$RET")" || true
			setconfig directory "$mydirectory"
			if [ -z "$mydirectory" ]; then
				echo INITREMOTE-FAILURE "You need to set directory="
			else
				if mkdir -p "$mydirectory"; then
					setupcreds
				else
					echo INITREMOTE-FAILURE "Failed to write to $mydirectory"
				fi
			fi
		;;
		PREPARE)
			# Use GETCONFIG to get configuration settings,
			# and do anything needed to get ready for using the
			# special remote here.
			getcreds
			getconfig directory
			mydirectory="$RET"
			if [ -d "$mydirectory" ]; then
				echo PREPARE-SUCCESS
			else
				echo PREPARE-FAILURE "$mydirectory not found"
			fi
		;;
		TRANSFER)
			op="$2"
			key="$3"
			shift 3
			file="$@"
			case "$op" in
				STORE)
					# Store the file to a location
					# based on the key.
					calclocation "$key"
					dostore "$key" "$file" "$LOC"
				;;
				RETRIEVE)
					# Retrieve from a location based on
					# the key, outputting to the file.
					calclocation "$key"
					doretrieve "$key" "$file" "$LOC"
				;;
			esac
		;;
		CHECKPRESENT)
			key="$2"
			calclocation "$key"
			docheckpresent "$key" "$LOC"
		;;
		REMOVE)
			key="$2"
			calclocation "$key"
			doremove "$key" "$LOC"
		;;
		# The requests listed above are all the ones
		# that are required to be supported, so it's fine
		# to respond to any others with UNSUPPORTED-REQUEST.

		# Let's also support exporting...
		EXPORTSUPPORTED)
			echo EXPORTSUPPORTED-SUCCESS
		;;
		EXPORT)
			shift 1
			exportlocation="$mydirectory/$@"
			# No response to this one; this value is used below.
		;;
		TRANSFEREXPORT)
			op="$2"
			key="$3"
			shift 3
			file="$@"
			case "$op" in
				STORE)
					# Store the file to the exportlocation
					dostore "$key" "$file" "$exportlocation"
				;;
				RETRIEVE)
					# Retrieve from the exportlocation,
					# outputting to the file.
					doretrieve "$key" "$exportlocation" "$file"
				;;
			esac
		;;
		CHECKPRESENTEXPORT)
			key="$2"
			docheckpresent "$key" "$exportlocation"
		;;
		REMOVEEXPORT)
			key="$2"
			doremove "$key" "$exportlocation"
		;;
		RENAMEEXPORT)
			key="$2"
			shift 2
			newexportlocation="$mydirectory/$@"
			mkdir -p "$(dirname "$newexportlocation")"
			if runcmd mv -f "$exportlocation" "$newexportlocation"; then
				echo RENAMEEXPORT-SUCCESS "$key"
			else
				echo RENAMEEXPORT-FAILURE "$key"
			fi
		;;

		*)
			echo UNSUPPORTED-REQUEST
		;;
	esac	
done

# XXX anything that needs to be done at shutdown can be done here