@@ -20,15 +20,15 @@ import org.springframework.beans.factory.annotation.Autowired; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.concurrent.TimeUnit; | |||
import static java.util.concurrent.TimeUnit.MINUTES; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||
import static org.cobbzilla.util.network.NetworkUtil.IPv4_ALL_ADDRS; | |||
public abstract class DnsDriverBase<T> extends CloudServiceDriverBase<T> implements DnsServiceDriver { | |||
private static final long DNS_LOCK_TIMEOUT = TimeUnit.MINUTES.toMillis(1); | |||
private static final long DNS_DEADLOCK_TIMEOUT = TimeUnit.MINUTES.toMillis(5); | |||
private static final long DNS_LOCK_TIMEOUT = MINUTES.toMillis(1); | |||
private static final long DNS_DEADLOCK_TIMEOUT = MINUTES.toMillis(5); | |||
@Autowired protected BubbleDomainDAO domainDAO; | |||
@Autowired protected BubbleNetworkDAO networkDAO; | |||
@@ -36,10 +36,12 @@ public abstract class DnsDriverBase<T> extends CloudServiceDriverBase<T> impleme | |||
@Getter(lazy=true) private final RedisService dnsLocks = redis.prefixNamespace(getClass().getSimpleName()+"_dns_lock"); | |||
protected synchronized String lockDomain(String domain) { | |||
// log.info("lockDomain: "+domain+" called from "+stacktrace()); | |||
return getDnsLocks().lock(domain, DNS_LOCK_TIMEOUT, DNS_DEADLOCK_TIMEOUT); | |||
} | |||
protected synchronized void unlockDomain(String domain, String lock) { | |||
// log.info("unlockDomain: "+domain+" called from "+stacktrace()); | |||
getDnsLocks().unlock(domain, lock); | |||
} | |||
@@ -119,4 +119,7 @@ public class Device extends IdentifiableBase implements HasAccount { | |||
// make ctime visible | |||
@JsonProperty public long getCtime () { return super.getCtime(); } | |||
@Transient @Getter @Setter private DeviceStatus status; | |||
public boolean hasStatus () { return status != null && status.hasIp(); } | |||
} |
@@ -0,0 +1,134 @@ | |||
package bubble.model.device; | |||
import bubble.cloud.geoLocation.GeoLocation; | |||
import bubble.service.cloud.GeoService; | |||
import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
import lombok.experimental.Accessors; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.wizard.cache.redis.RedisService; | |||
import static java.util.concurrent.TimeUnit.MINUTES; | |||
import static java.util.concurrent.TimeUnit.SECONDS; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.now; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError; | |||
@NoArgsConstructor @Accessors(chain=true) @Slf4j | |||
public class DeviceStatus { | |||
public static final DeviceStatus NO_DEVICE_STATUS = new DeviceStatus(); | |||
@Getter @Setter private String ip; | |||
public boolean hasIp () { return ip != null; } | |||
@Getter @Setter private int port; | |||
@Getter @Setter private GeoLocation location; | |||
@Getter @Setter private String bytesSent; | |||
@Getter @Setter private String sentUnits; | |||
@Getter @Setter private String bytesReceived; | |||
@Getter @Setter private String receivedUnits; | |||
@Getter @Setter private Integer lastHandshakeMinutes; | |||
@Getter @Setter private Integer lastHandshakeSeconds; | |||
@Getter @Setter private Long lastHandshakeTime; | |||
public static final String DEVICE_STATUS_PREFIX = "wg_device_status_"; | |||
public static final String DEVICE_STATUS_ENDPOINT_SUFFIX = "_endpoint"; | |||
public static final String DEVICE_STATUS_TRANSFER_SUFFIX = "_transfer"; | |||
public static final String DEVICE_STATUS_HANDSHAKE_SUFFIX = "_latestHandshake"; | |||
public DeviceStatus(RedisService redis, String geoAccount, GeoService geoService, String deviceUuid) { | |||
final String endpoint = redis.get_plaintext(DEVICE_STATUS_PREFIX+deviceUuid+DEVICE_STATUS_ENDPOINT_SUFFIX); | |||
if (endpoint != null) { | |||
try { | |||
final int lastColon = endpoint.lastIndexOf(':'); | |||
if (lastColon != -1) { | |||
setIp(endpoint.substring(0, lastColon)); | |||
setPort(Integer.parseInt(endpoint.substring(lastColon + 1))); | |||
if (geoService != null) { | |||
try { | |||
setLocation(geoService.locate(geoAccount, getIp())); | |||
} catch (Exception e) { | |||
log.error("DeviceStatus: error calling geoService for ip="+getIp()+": "+shortError(e)); | |||
} | |||
} | |||
} | |||
} catch (Exception e) { | |||
log.error("DeviceStatus: error parsing endpoint: "+endpoint+": "+shortError(e)); | |||
} | |||
} | |||
final String transfer = redis.get_plaintext(DEVICE_STATUS_PREFIX+deviceUuid+DEVICE_STATUS_TRANSFER_SUFFIX); | |||
if (transfer != null) { | |||
try { | |||
final String[] parts = transfer.split("\\s+"); | |||
if (parts.length != 6) { | |||
log.error("DeviceStatus: error parsing transfer: " + transfer); | |||
} else { | |||
if (!parts[2].equals("received,")) throw new IllegalArgumentException("expected 'received,' in parts[2]"); | |||
if (!parts[5].equals("sent")) throw new IllegalArgumentException("expected 'sent' in parts[5]"); | |||
setBytesReceived(parts[0]); | |||
setReceivedUnits(parseUnits(parts[1])); | |||
setBytesSent(parts[3]); | |||
setSentUnits(parseUnits(parts[4])); | |||
} | |||
} catch (Exception e) { | |||
log.error("DeviceStatus: error parsing transfer: "+transfer+": "+shortError(e)); | |||
} | |||
} | |||
final String handshake = redis.get_plaintext(DEVICE_STATUS_PREFIX+deviceUuid+DEVICE_STATUS_HANDSHAKE_SUFFIX); | |||
if (handshake == null) { | |||
if (transfer != null) { | |||
log.warn("DeviceStatus: transfer found but no handshake info for device "+deviceUuid); | |||
} | |||
} else { | |||
try { | |||
final String[] parts = handshake.split("\\s+"); | |||
if (!parts[parts.length-1].equals("ago")) { | |||
log.error("DeviceStatus: error parsing handshake, expected 'ago' as last token"); | |||
} else { | |||
if (parts.length == 3) { | |||
if (parts[1].startsWith("minute")) { | |||
setLastHandshakeMinutes(Integer.parseInt(parts[0])); | |||
setLastHandshakeSeconds(0); | |||
initLastHandshakeTime(); | |||
} else if (parts[1].startsWith("second")) { | |||
setLastHandshakeSeconds(Integer.parseInt(parts[0])); | |||
setLastHandshakeMinutes(0); | |||
initLastHandshakeTime(); | |||
} else { | |||
log.error("DeviceStatus: error parsing handshake, expected 'minutes' or 'seconds' in parts[1]: "+handshake); | |||
} | |||
} else if (parts.length == 5) { | |||
if (!parts[1].startsWith("minute")) { | |||
log.error("DeviceStatus: error parsing handshake, expected 'minute' or 'minutes' in parts[1]: "+handshake); | |||
} else if (!parts[3].startsWith("second")) { | |||
log.error("DeviceStatus: error parsing handshake, expected 'second' or 'seconds' in parts[3]: "+handshake); | |||
} else { | |||
setLastHandshakeMinutes(Integer.valueOf(parts[0])); | |||
setLastHandshakeSeconds(Integer.valueOf(parts[2])); | |||
initLastHandshakeTime(); | |||
} | |||
} else { | |||
log.error("DeviceStatus: error parsing handshake: "+handshake+": expected 3 or 5 parts, found "+parts.length); | |||
} | |||
} | |||
} catch (Exception e) { | |||
log.error("DeviceStatus: error parsing handshake: "+handshake+": "+shortError(e)); | |||
} | |||
} | |||
} | |||
private String parseUnits(String units) { | |||
if (units.equalsIgnoreCase("b")) return "b"; | |||
return units.substring(0, 1).toUpperCase(); | |||
} | |||
private void initLastHandshakeTime() { | |||
setLastHandshakeTime(now() - MINUTES.toMillis(getLastHandshakeMinutes()) - SECONDS.toMillis(getLastHandshakeSeconds())); | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
package bubble.model.device; | |||
import java.util.Comparator; | |||
public class DeviceStatusFirstComparator implements Comparator<Device> { | |||
public static DeviceStatusFirstComparator DEVICE_WITH_STATUS_FIRST = new DeviceStatusFirstComparator(); | |||
@Override public int compare(Device d1, Device d2) { | |||
if (d1.hasStatus() && d2.hasStatus()) { | |||
return Long.compare(d1.getCtime(), d2.getCtime()); | |||
} | |||
if (d1.hasStatus()) return -1; | |||
if (d2.hasStatus()) return 1; | |||
return Long.compare(d1.getCtime(), d2.getCtime()); | |||
} | |||
} |
@@ -66,7 +66,9 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned | |||
return ok(populate(ctx, list(req, ctx))); | |||
} | |||
protected List<E> list(Request req, ContainerRequest ctx) { return list(ctx); } | |||
protected List<E> list(Request req, ContainerRequest ctx) { return sort(list(ctx), req, ctx); } | |||
protected List<E> sort(List<E> list, Request req, ContainerRequest ctx) { return list; } | |||
protected String getAccountUuid(ContainerRequest ctx) { return getAccountUuid(account, ctx); } | |||
@@ -10,7 +10,9 @@ import bubble.model.device.Device; | |||
import bubble.model.device.DeviceSecurityLevel; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.cloud.DeviceIdService; | |||
import edu.emory.mathcs.backport.java.util.Collections; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.glassfish.grizzly.http.server.Request; | |||
import org.glassfish.jersey.server.ContainerRequest; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
@@ -24,6 +26,7 @@ import java.util.List; | |||
import java.util.stream.Collectors; | |||
import static bubble.ApiConstants.*; | |||
import static bubble.model.device.DeviceStatusFirstComparator.DEVICE_WITH_STATUS_FIRST; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
@@ -41,11 +44,22 @@ public class DevicesResource extends AccountOwnedResource<Device, DeviceDAO> { | |||
@Override protected List<Device> list(ContainerRequest ctx) { | |||
final Account caller = userPrincipal(ctx); | |||
final List<Device> devices; | |||
if (caller.admin() && ctx.getRequestUri().getQuery() != null && ctx.getRequestUri().getQuery().contains("all")) { | |||
return getDao().findAll().stream().filter(Device::initialized).collect(Collectors.toList()); | |||
devices = getDao().findAll().stream().filter(Device::initialized).collect(Collectors.toList()); | |||
} else { | |||
return super.list(ctx).stream().filter(Device::initialized).collect(Collectors.toList()); | |||
devices = super.list(ctx).stream().filter(Device::initialized).collect(Collectors.toList()); | |||
} | |||
return devices; | |||
} | |||
@Override protected Device populate(ContainerRequest ctx, Device device) { | |||
return device.setStatus(deviceIdService.getDeviceStatus(device.getUuid())); | |||
} | |||
@Override protected List<Device> sort(List<Device> list, Request req, ContainerRequest ctx) { | |||
Collections.sort(list, DEVICE_WITH_STATUS_FIRST); | |||
return list; | |||
} | |||
@Override protected boolean canChangeName() { return true; } | |||
@@ -103,4 +117,12 @@ public class DevicesResource extends AccountOwnedResource<Device, DeviceDAO> { | |||
return ok(deviceIdService.findIpsByDevice(device.getUuid())); | |||
} | |||
@GET @Path("/{id}"+EP_STATUS) | |||
public Response getStatus(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final Device device = getDao().findByAccountAndId(getAccountUuid(ctx), id); | |||
if (device == null) return notFound(id); | |||
return ok(deviceIdService.getLiveDeviceStatus(device.getUuid())); | |||
} | |||
} |
@@ -5,6 +5,7 @@ | |||
package bubble.service.cloud; | |||
import bubble.model.device.Device; | |||
import bubble.model.device.DeviceStatus; | |||
import java.util.List; | |||
@@ -18,4 +19,7 @@ public interface DeviceIdService { | |||
void setDeviceSecurityLevel(Device device); | |||
DeviceStatus getDeviceStatus(String deviceUuid); | |||
DeviceStatus getLiveDeviceStatus(String deviceUuid); | |||
} |
@@ -7,6 +7,7 @@ package bubble.service.cloud; | |||
import bubble.dao.account.AccountDAO; | |||
import bubble.dao.device.DeviceDAO; | |||
import bubble.model.device.Device; | |||
import bubble.model.device.DeviceStatus; | |||
import bubble.server.BubbleConfiguration; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.collection.ExpirationMap; | |||
@@ -24,6 +25,7 @@ import java.util.List; | |||
import java.util.Map; | |||
import static bubble.ApiConstants.HOME_DIR; | |||
import static bubble.model.device.DeviceStatus.NO_DEVICE_STATUS; | |||
import static java.util.concurrent.TimeUnit.MINUTES; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
@@ -45,6 +47,7 @@ public class StandardDeviceIdService implements DeviceIdService { | |||
@Autowired private DeviceDAO deviceDAO; | |||
@Autowired private AccountDAO accountDAO; | |||
@Autowired private RedisService redis; | |||
@Autowired private GeoService geoService; | |||
@Autowired private BubbleConfiguration configuration; | |||
private final Map<String, Device> deviceCache = new ExpirationMap<>(MINUTES.toMillis(10)); | |||
@@ -117,6 +120,17 @@ public class StandardDeviceIdService implements DeviceIdService { | |||
} | |||
} | |||
private final ExpirationMap<String, DeviceStatus> deviceStatusCache = new ExpirationMap<>(MINUTES.toMillis(2)); | |||
@Override public DeviceStatus getDeviceStatus(String deviceUuid) { | |||
return deviceStatusCache.computeIfAbsent(deviceUuid, k -> getLiveDeviceStatus(deviceUuid)); | |||
} | |||
@Override public DeviceStatus getLiveDeviceStatus(String deviceUuid) { | |||
if (configuration.testMode()) return NO_DEVICE_STATUS; | |||
return new DeviceStatus(redis, accountDAO.getFirstAdmin().getUuid(), geoService, deviceUuid); | |||
} | |||
private Device findTestDevice(String ipAddr) { | |||
final String adminUuid = accountDAO.getFirstAdmin().getUuid(); | |||
final List<Device> adminDevices = deviceDAO.findByAccount(adminUuid); | |||
@@ -5,6 +5,7 @@ | |||
package bubble.service_dbfilter; | |||
import bubble.model.device.Device; | |||
import bubble.model.device.DeviceStatus; | |||
import bubble.service.cloud.DeviceIdService; | |||
import org.springframework.stereotype.Service; | |||
@@ -23,4 +24,7 @@ public class DbFilterDeviceIdService implements DeviceIdService { | |||
@Override public void setDeviceSecurityLevel(Device device) { notSupported("setDeviceSecurityLevel"); } | |||
@Override public DeviceStatus getDeviceStatus(String deviceUuid) { return notSupported("getDeviceStats"); } | |||
@Override public DeviceStatus getLiveDeviceStatus(String deviceUuid) { return notSupported("getLiveDeviceStatus"); } | |||
} |
@@ -1 +1 @@ | |||
bubble.version=0.13.1 | |||
bubble.version=0.13.2 |
@@ -469,7 +469,12 @@ label_field_device_app=App | |||
label_field_device_certificate=Certificate | |||
label_field_device_enabled=Enabled? | |||
label_field_device_vpn_config=VPN | |||
label_field_security_level=Security Level | |||
label_field_device_security_level=Security Level | |||
label_field_device_connection=Connection | |||
label_field_device_connection_handshake=Last handshake | |||
label_field_device_connection_handshake_ago=ago | |||
label_field_device_transfer_sent=sent | |||
label_field_device_transfer_received=received | |||
label_field_device_ctime=Added | |||
label_device_ctime_format={{MMM}} {{d}}, {{YYYY}} / {{h}}:{{m}}{{a}} | |||
message_device_vpn_show_config=Show VPN connection info | |||
@@ -165,6 +165,20 @@ price_period_yearly_name=annually | |||
price_period_yearly_unit=year | |||
price_format={{messages['currency_symbol_'+currency.toUpperCase()]}}{{priceMajorUnits}}{{priceMinorUnits === 0 ? '' : priceMinorUnits < 10 ? '.0'+priceMinorUnits : '.'+priceMinorUnits}} {{messages['price_period_'+period+'_name']}} | |||
# Time units | |||
units_second=second | |||
units_seconds=seconds | |||
units_seconds_short=s | |||
units_minute=minute | |||
units_minutes=minutes | |||
units_minutes_short=m | |||
units_hour=hour | |||
units_hours=hours | |||
units_hours_short=h | |||
units_day=day | |||
units_days=days | |||
units_days_short=d | |||
currency_symbol_= | |||
currency_symbol_USD=$ | |||
@@ -22,45 +22,90 @@ fi | |||
while : ; do | |||
peer="" | |||
device="" | |||
endpoint="" | |||
latest_handshake="" | |||
transfer="" | |||
IFS=$'\n' | |||
for line in $(wg show all) ; do | |||
if [[ ! -z "${peer}" ]] ; then | |||
if [[ $(echo "${line}" | tr -d ' ') == allowed* ]] ; then | |||
for ip in $(echo "${line}" | cut -d: -f2- | tr ',' '\n' | tr -d ' ' | cut -d/ -f1) ; do | |||
device_uuids="$(find $(find $(find ${ALGO_CONFIGS} -type d -name wireguard) -type d -name public) -type f | xargs grep -l ${peer} | xargs -n 1 basename)" | |||
if [[ $(echo "${device_uuids}" | wc -l | tr -d ' ') -gt 1 ]] ; then | |||
log "Multiple device UUIDs found for IP ${ip} (not recording anything): ${device_uuids}" | |||
continue | |||
fi | |||
device="$(echo "${device_uuids}" | head -1 | tr -d ' ')" | |||
if [[ ! -z "${peer}" ]] ; then | |||
if [[ $(echo "${line}" | tr -d ' ') == endpoint* ]] ; then | |||
endpoint="$(echo "${line}" | cut -d: -f2- | awk '{$1=$1};1')" | |||
ip_file="${BUBBLE_DEVICE_DIR}/ip_$(echo ${ip})" | |||
if [[ ! -f ${ip_file} ]] ; then | |||
touch ${ip_file} && chown bubble ${ip_file} && chmod 400 ${ip_file} || log "Error creating ${ip_file}" | |||
fi | |||
device_exists=$(grep -c "${ip}" ${ip_file}) | |||
if [[ ${device_exists} -eq 0 ]] ; then | |||
log "recorded device ${device} for IP ${ip}" | |||
echo "${device}" > ${ip_file} || log "Error writing ${device} to ${ip_file}" | |||
fi | |||
elif [[ $(echo "${line}" | tr -d ' ') == latest* ]] ; then | |||
latest_handshake="$(echo "${line}" | cut -d: -f2- | awk '{$1=$1};1')" | |||
device_file="${BUBBLE_DEVICE_DIR}/device_$(echo ${device})" | |||
if [[ ! -f ${device_file} ]] ; then | |||
touch ${device_file} && chown bubble ${device_file} && chmod 400 ${device_file} || log "Error creating ${ip_file}" | |||
fi | |||
ip_exists=$(grep -c "${ip}" ${device_file}) | |||
if [[ ${ip_exists} -eq 0 ]] ; then | |||
log "recorded IP ${ip} for device ${device}" | |||
echo "${ip}" >> ${device_file} || log "Error writing ${ip} to ${device_file}" | |||
fi | |||
elif [[ $(echo "${line}" | tr -d ' ') == transfer* ]] ; then | |||
transfer="$(echo "${line}" | cut -d: -f2- | awk '{$1=$1};1')" | |||
done | |||
peer="" | |||
fi | |||
elif [[ $(echo "${line}" | tr -d ' ') == allowed* ]] ; then | |||
for ip in $(echo "${line}" | cut -d: -f2- | tr ',' '\n' | tr -d ' ' | cut -d/ -f1) ; do | |||
device_uuids="$(find $(find $(find ${ALGO_CONFIGS} -type d -name wireguard) -type d -name public) -type f | xargs grep -l ${peer} | xargs -n 1 basename)" | |||
if [[ $(echo "${device_uuids}" | wc -l | tr -d ' ') -gt 1 ]] ; then | |||
log "Multiple device UUIDs found for IP ${ip} (not recording anything): ${device_uuids}" | |||
continue | |||
fi | |||
device="$(echo "${device_uuids}" | head -1 | tr -d ' ')" | |||
ip_file="${BUBBLE_DEVICE_DIR}/ip_$(echo ${ip})" | |||
if [[ ! -f ${ip_file} ]] ; then | |||
touch ${ip_file} && chown bubble ${ip_file} && chmod 400 ${ip_file} || log "Error creating ${ip_file}" | |||
fi | |||
device_exists=$(grep -c "${ip}" ${ip_file}) | |||
if [[ ${device_exists} -eq 0 ]] ; then | |||
# log "recorded device ${device} for IP ${ip}" | |||
echo "${device}" > ${ip_file} || log "Error writing ${device} to ${ip_file}" | |||
fi | |||
device_file="${BUBBLE_DEVICE_DIR}/device_$(echo ${device})" | |||
if [[ ! -f ${device_file} ]] ; then | |||
touch ${device_file} && chown bubble ${device_file} && chmod 400 ${device_file} || log "Error creating ${ip_file}" | |||
fi | |||
ip_exists=$(grep -c "${ip}" ${device_file}) | |||
if [[ ${ip_exists} -eq 0 ]] ; then | |||
# log "recorded IP ${ip} for device ${device}" | |||
echo "${ip}" >> ${device_file} || log "Error writing ${ip} to ${device_file}" | |||
fi | |||
done | |||
fi | |||
fi | |||
elif [[ ${line} == peer* ]] ; then | |||
peer="$(echo "${line}" | awk '{print $NF}')" | |||
if [[ ${line} == peer* ]] ; then | |||
if [[ ! -z "${peer}" ]] ; then | |||
if [[ ! -z "${device}" ]] ; then | |||
echo "in-loop, setting stats for peer ${peer} device ${device}" | |||
if [[ ! -z "${endpoint}" ]] ; then | |||
echo "set wg_device_status_${device}_endpoint \"${endpoint}\"" | redis-cli | |||
fi | |||
if [[ ! -z "${latest_handshake}" ]] ; then | |||
echo "set wg_device_status_${device}_latestHandshake \"${latest_handshake}\"" | redis-cli | |||
fi | |||
if [[ ! -z "${transfer}" ]] ; then | |||
echo "set wg_device_status_${device}_transfer \"${transfer}\"" | redis-cli | |||
fi | |||
fi | |||
fi | |||
peer="$(echo "${line}" | awk '{print $NF}')" | |||
device="" | |||
endpoint="" | |||
latest_handshake="" | |||
transfer="" | |||
echo "in-loop, set peer: ${peer}" | |||
fi | |||
done | |||
sleep 30s | |||
if [[ ! -z "${peer}" ]] ; then | |||
echo "end-of-loop, setting stats for peer ${peer} device ${device}" | |||
if [[ ! -z "${device}" ]] ; then | |||
if [[ ! -z "${endpoint}" ]] ; then | |||
echo "set wg_device_status_${device}_endpoint \"${endpoint}\"" | redis-cli | |||
fi | |||
if [[ ! -z "${latest_handshake}" ]] ; then | |||
echo "set wg_device_status_${device}_latestHandshake \"${latest_handshake}\"" | redis-cli | |||
fi | |||
if [[ ! -z "${transfer}" ]] ; then | |||
echo "set wg_device_status_${device}_transfer \"${transfer}\"" | redis-cli | |||
fi | |||
fi | |||
fi | |||
sleep 10s | |||
done |
@@ -4,14 +4,16 @@ | |||
# | |||
TARGET_FILE=${1:?no target file provided} | |||
TIMEOUT=${2:?no timeout provided} | |||
LOG=/tmp/ensure_file_$(echo ${TARGET_FILE} | tr '/' '_').log | |||
start=$(date +%s) | |||
while [[ ! -f ${TARGET_FILE} && $(expr $(date +%s) - ${start}) -le ${TIMEOUT} ]] ; do | |||
echo "$(date): $0: waiting for target file to exist ${TARGET_FILE} (will timeout after ${TIMEOUT} seconds)" | |||
echo "$(date): $0: waiting for target file to exist ${TARGET_FILE} (will timeout after ${TIMEOUT} seconds)" | tee -a ${LOG} | |||
sleep 1s | |||
done | |||
if [[ ! -f ${TARGET_FILE} ]] ; then | |||
echo "target file did not get created: ${TARGET_FILE} (timeout after ${TIMEOUT} seconds)" | |||
echo "target file did not get created: ${TARGET_FILE} (timeout after ${TIMEOUT} seconds)" | tee -a ${LOG} | |||
exit 1 | |||
fi | |||
echo "target file has been created: ${TARGET_FILE}" | tee -a ${LOG} |
@@ -1 +1 @@ | |||
Subproject commit 6095552f8359790d073a891eb4bcbd31bb1db3ed | |||
Subproject commit ddc83845f63066c5388e9e5fa97735a5592fcd98 |
@@ -1 +1 @@ | |||
Subproject commit 77a8875358d5878da929783862d70d023bb3ad40 | |||
Subproject commit 009a52edb53315fcb7d90c9feed382970aa9a4b8 |