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
|
#!/bin/bash
#/**
# checkrepo
#@file checkrepo
#@brief Maintain repository information in a JSON file.
#@warning This script uses "jq" command.
#@version 1.1.0 - Initial version.
#@author Ramón M. Gómez - ETSII Univ. Sevilla
#@date 2017-09-27
#*/ ##
# Global constants definition.
PROG=$(basename "$(realpath "$0")")
OPENGNSYS=/opt/opengnsys
IMAGESDIR=$OPENGNSYS/images
INFOFILE=$OPENGNSYS/etc/repoinfo.json
# Auxiliar functions.
# Metafunction to check if JSON result exists.
function jq() {
local OUTPUT
OUTPUT=$($JQ "$@") || return $?
[[ "$OUTPUT" = "null" ]] && return 1
echo "$OUTPUT"
}
# Create/edit JSON file about installed ogLive clients.
function addToJson() {
# Parameters and variables.
local IMAGENAME="$1" IMAGETYPE="$2" DATA="$3" JSON i j n m OUNAME OUIND IMGIND
local CLONATOR COMPRESSOR FSTYPE DATASIZE CLIENT
IFS=":" read -r CLONATOR COMPRESSOR FSTYPE DATASIZE CLIENT <<<"$DATA"
# Check if image is restricted to an OU (subdir).
if [[ $IMAGENAME =~ / ]]; then
OUNAME="${IMAGENAME%/*}"
IMAGENAME="${IMAGENAME##*/}"
fi
# Data size must be numeric (in KB).
[[ $DATASIZE =~ ^[0-9]*$ ]] || DATASIZE=0
# JSON-formatted new entry.
JSON=$(cat << EOT | jq .
{
"name":"$IMAGENAME",
"type":"${IMAGETYPE,,}",
"clientname":"$CLIENT",
"clonator":"${CLONATOR,,}",
"compressor":"${COMPRESSOR,,}",
"filesystem":"${FSTYPE^^}",
"datasize":$[ DATASIZE * 1024]
}
EOT
)
# Check JSON file consistency.
if [ "$(jq -c keys $INFOFILE 2>/dev/null)" == '["directory","images","ous"]' ]; then
# Common image.
if [ -z "$OUNAME" ]; then
# Check if the image is defined into JSON file.
n=$(jq ".images | length" $INFOFILE)
for ((i=0; i<n; i++)); do
[ "$(jq ".check=$JSON | .check.name==.images[$i].name" $INFOFILE)" == "true" ] && IMGIND=$i
done
# Check if it needs to update or insert data.
if [ -n "$IMGIND" ]; then
# Update if image data changes and info file exists.
[ -n "$3" -a "$(jq ".check=$JSON | .check==.images[$IMGIND]" $INFOFILE)" == "false" ] && jq ".images[$IMGIND]=$JSON" $INFOFILE | sponge $INFOFILE
else
# Append a new entry.
jq ".images |= (. + [$JSON])" $INFOFILE | sponge $INFOFILE
fi
else # OU image.
# Append a new OU entry if it does not exist.
if [ -z "$(jq -r ".ous[].subdir" $INFOFILE | grep "^$OUNAME$")" ]; then
JSON=$(cat << EOT | jq .
{
"subdir": "$OUNAME",
"images": [ $JSON ]
}
EOT
)
jq ".ous |= (. + [$JSON])" $INFOFILE | sponge $INFOFILE
else
# Check if the image is defined in some OU.
m=$(jq ".ous | length" $INFOFILE)
for ((j=0; j<m; j++)); do
n=$(jq ".ous[$j].images | length" $INFOFILE)
for ((i=0; i<n; i++)); do
[ "$(jq ".check=$JSON | .check.name==.ous[$j].images[$i].name" $INFOFILE)" == "true" ] && OUIND=$j && IMGIND=$i
done
done
# Check if it needs to update or insert data.
if [ -n "$IMGIND" ]; then
# Update if image data changes and info file exists.
[ $# -gt 2 -a "$(jq ".check=$JSON | .check==.ous[$OUIND].images[$IMGIND]" $INFOFILE)" == "false" ] && jq ".ous[$OUIND].images[$IMGIND]=$JSON" $INFOFILE | sponge $INFOFILE
else
# Append a new entry.
jq ".ous[$OUIND].images |= (. + [$JSON])" $INFOFILE | sponge $INFOFILE
fi
fi
fi
else
# Create new JSON file.
if [ -z "$OUNAME" ]; then
cat << EOT | jq . > $INFOFILE
{"directory":"$IMAGESDIR","images":[${IMAGENAME:+$JSON}],"ous":[]}
EOT
else
cat << EOT | jq . > $INFOFILE
{"directory":"$IMAGESDIR","images":[],"ous":[{"subdir":"$OUNAME","images":[$JSON]}]}
EOT
fi
fi
}
# Show an error message.
function raiseError() {
case "$1" in
usage)
echo "$PROG: Usage error: Type \"$PROG help\"" >&2
exit 1 ;;
notfound)
echo "$PROG: Resource not found: $2" >&2
exit 2 ;;
access)
echo "$PROG: Access error: $2" >&2
exit 3 ;;
*)
echo "$PROG: Unknown error" >&2
exit 1 ;;
esac
}
# Command functions.
# Show help message.
function help() {
cat << EOT
$PROG: maintain the repository information.
Usage: $PROG
EOT
}
# Check for file-based images to update the repository configuration file.
function checkfiles() {
local IMAGES IMG INFO DATA
# File-stored images.
IMAGES=$(find $IMAGESDIR -maxdepth 2 -type f \( -name "*.img" -o -name "*.dsk" \) -print)
for IMG in $IMAGES; do
# Skip locked images.
[ -e "$IMG.lock" ] && continue
# Retrieve image creation data and delete temporary file.
INFO="$IMG.info"
[ -e "$INFO" -a "$INFO" -ot "$IMG" ] && rm -f "$INFO" && echo "Warning: Deleted outdated file $INFO"
DATA=""
[ -r "$INFO" ] && DATA=$(cat "$INFO")
# Add data to configuration file (name, type and data) and remove image info file.
IMG=${IMG#$IMAGESDIR/}
addToJson "${IMG%.*}" "${IMG##*.}" "$DATA" && rm -f "$INFO"
done
}
# Check for directory-based images to update the repository configuration file.
function checkdirs() {
local IMAGES IMG INFO DATA
# Directory-based images.
IMAGES=$(find $IMAGESDIR -maxdepth 3 -type f -name ogimg.info -print)
for INFO in $IMAGES; do
IMG="$(dirname "${INFO#$IMAGESDIR/}")"
# Skip repository root directory and locked images.
[ "$IMG" == "$IMAGESDIR" -o -e "$IMG.lock" ] && continue
DATA=$(awk -F= '$1=="# fstype" {fs=$2} $1=="# sizedata" {sz=$2} END {printf "rsync::%s:%s:",fs,sz}' "$INFO")
# Add data to configuration file (name, type and data).
addToJson "$IMG" "dir" "$DATA"
done
}
# Check if images are removed to update the repository configuration file.
function checkremoved() {
local IMG TYPE OU i j n m
[ ! -w "$INFOFILE" ] && raiseError access "$INFOFILE"
# Check if global images are defined into JSON file.
n=$(jq ".images | length" $INFOFILE)
for ((i=0; i<n; i++)); do
# Image name and type.
IMG="$(jq -r ".images[$i].name" $INFOFILE)"
TYPE="$(jq -r ".images[$i].type" $INFOFILE)"
[ "$TYPE" != "dir" ] && IMG="$IMG.$TYPE"
# Delete entry if image does not exist and it's not locked.
[ ! -e "$IMAGESDIR/$IMG" -a ! -e "$IMAGESDIR/$IMG.lock" ] && jq "del(.images[$i])" $INFOFILE | sponge $INFOFILE
done
# Check if OU images are defined into JSON file.
m=$(jq ".ous | length" $INFOFILE)
for ((j=0; j<m; j++)); do
# OU subdir.
OU="$(jq -r ".ous[$j].subdir" $INFOFILE)"
# Delete OU's entries if its subdir does not exist.
if [ ! -e "$IMAGESDIR/$OU" ]; then
jq "del(.ous[$j])" $INFOFILE | sponge $INFOFILE
else
n=$(jq ".images | length" $INFOFILE)
for ((i=0; i<n; i++)); do
# Image name and type.
IMG="$(jq -r ".ous[$j].images[$i].name" $INFOFILE)"
TYPE="$(jq -r ".ous[$j].images[$i].type" $INFOFILE)"
[ "$TYPE" != "dir" ] && IMG="$IMG.$TYPE"
# Delete entry if image does not exist and it's not locked.
[ ! -e "$IMAGESDIR/$OU/$IMG" -a ! -e "$IMAGESDIR/$OU/$IMG.lock" ] && jq "del(.ous[$j].images[$i])" $INFOFILE | sponge $INFOFILE
done
fi
done
}
# Main progrram.
# Check dependencies.
JQ=$(which jq 2>/dev/null) || raiseError notfound "Need to install \"jq\"."
which sponge &>/dev/null || raiseError notfound "Need to install \"moreutils\"."
[ ! -w "$(dirname "$INFOFILE")" ] && raiseError access "$INFOFILE"
[ -e "$INFOFILE" ] || addToJson
checkfiles
checkdirs
checkremoved
|