/*  provide.c: provide status infos.
    Copyright (C) 2009 AVM

    This file is part of davfs2.

    davfs2 is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    davfs2 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with davfs2; if not, write to the Free Software Foundation,
    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */

#include "config.h"

#include <errno.h>
#include <error.h>
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <string.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#include <sys/shm.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/wait.h>

#include "defaults.h"

#include "mount_davfs.h"
#include "provide.h"
#include "webdav_shm.h"

/* These infos are stored in a shared memory to provide
 * them for other processes. */
static struct webdav_file_infos sinfos;
static struct webdav_file_infos * infos = &sinfos;


/* Debug-Level */
static int debug = 0;


#define TRANS_SPEED_INTERVAL 60
#define TRANS_SPEED_SLOTS    5
struct trans_speed{
    uint64_t bytes;
    double seconds;
    time_t start_time;
    struct trans_speed* next;
};

static struct trans_speed* download_speed_values = 0;
static struct trans_speed* upload_speed_values = 0;

static int write_file_webdav(struct webdav_file_infos * webdav_infos);
static int write_file_webdav_connection_state(struct webdav_file_infos * webdav_infos);


void print_speed(void);
void clear_speed_values(int type);
void delete_speed_values(int type);
void append_speed_value(int type, uint64_t bytes, double seconds);
void calculate_avg_speed(int type);
void is_low_upload_speed(void);

static void shm_lock(int shmid);
static void shm_unlock(int shmid);

//-----------------------------------------------------

void
provide_print(void)
{
    if(!debug || !infos)
        return;

    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "current status");
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- connection state:         %u", infos->connection_state);
    //info not longer here    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- cache speed:              %u", infos->cache_speed);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- cache storage avail:      %llu", infos->cache_storage_avail);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- cache storage used:       %llu", infos->cache_storage_used);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- dirty files:              %u", infos->dirty_files);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- running uploads:          %u", infos->running_uploads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- uploading file1:          %s", infos->uploading_file1);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- uploading file2:          %s", infos->uploading_file2);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- finished uploads:         %llu", infos->finished_uploads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- failed uploads:           %llu", infos->failed_uploads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- sum failed uploads:       %llu", infos->sum_failed_uploads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- running downloads:        %u", infos->running_downloads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- downloading file1:        %s", infos->downloading_file1);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- downloading file2:        %s", infos->downloading_file2);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- finished downloads:       %llu", infos->finished_downloads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- failed downloads:         %llu", infos->failed_downloads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- average upload speed:     %llu", infos->avg_upload_speed);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- average download speed:   %llu", infos->avg_download_speed);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- size uploads:             %llu", infos->size_uploads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- size downloads:           %llu", infos->size_downloads);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- storage quota available:  %llu", infos->storage_quota_avail);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- storage quota used:       %llu", infos->storage_quota_used);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- upload quota avail:       %llu", infos->upload_quota_avail);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- upload quota used:        %llu", infos->upload_quota_used);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- download quota avail:     %llu", infos->download_quota_avail);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- download quota used:      %llu", infos->download_quota_used);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- traffic quota avail:      %llu", infos->traffic_quota_avail);
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "- traffic quota used:       %llu", infos->traffic_quota_used);
}

int
provide_init(int debug_val)
{
	debug = debug_val;
	if(!infos)
		return -1;
	//cache speed is set in the start-script of davfs2
	memset(infos, 0, sizeof(struct webdav_file_infos));
	infos->is_running = 1;
	write_file_webdav (infos);
	return 0;
}

void
provide_destroy(void)
{
	if (infos)
		memset(infos, 0, sizeof(struct webdav_file_infos));
}

void
provide_set_uint(int type, unsigned int value, const char op)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set uint failed - shared memory missing");
        return;
    }
    unsigned int *old_val = 0;

    if(type == DIRTY_FILES)
        old_val = &infos->dirty_files;
    else if(type == RUNNING_UPLOADS)
        old_val = &infos->running_uploads;
    else if(type == RUNNING_DOWNLOADS)
        old_val = &infos->running_downloads;
    else{
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set uint failed - invalid parameter: type (%d)", type);
        return;
    }

    if(op == '+')
        *old_val += value;
    else if(op == '-')
        *old_val -= value;
    else
        *old_val = value;

    write_file_webdav (infos);
}

void
provide_set_uint64(int type, uint64_t value, const char op)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set uint64 failed - shared memory missing");
        return;
    }

    uint64_t *old_val = 0;

    if(type == FINISHED_UPLOADS)
        old_val = &infos->finished_uploads;
    else if(type == FAILED_UPLOADS)
        old_val = &infos->failed_uploads;
    else if(type == SUM_FAILED_UPLOADS)
        old_val = &infos->sum_failed_uploads;
    else if(type == FINISHED_DOWNLOADS)
        old_val = &infos->finished_downloads;
    else if(type == FAILED_DOWNLOADS)
        old_val = &infos->failed_downloads;
    else if(type == SIZE_UPLOADS)
        old_val = &infos->size_uploads;
    else if(type == SIZE_DOWNLOADS)
        old_val = &infos->size_downloads;
    else if(type == CACHE_STORAGE_USED)
        old_val = &infos->cache_storage_used;
    else if(type == CACHE_STORAGE_AVAIL)
        old_val = &infos->cache_storage_avail;
    else{
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set uint64 failed - invalid parameter: type (%d)", type);
        return;
    }

    if(op == '+')
        *old_val += value;
    else if(op == '-')
        *old_val -= value;
    else
        *old_val = value;

    write_file_webdav (infos);
}

void
provide_set_files(int type, const char* file1, const char* file2)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set files failed - shared memory missing");
        return;
    }

    if(type == UPLOAD_FILES){
        if(file1)
            snprintf(infos->uploading_file1, sizeof(infos->uploading_file1), "%s", file1);
        else
            memset(infos->uploading_file1, '\0', sizeof(infos->uploading_file1));
        if(file2)
            snprintf(infos->uploading_file2, sizeof(infos->uploading_file2), "%s", file2);
        else
            memset(infos->uploading_file2, '\0', sizeof(infos->uploading_file2));
    }
    else if(type == DOWNLOAD_FILES){
        if(file1)
            snprintf(infos->downloading_file1, sizeof(infos->downloading_file1), "%s", file1);
        else
            memset(infos->downloading_file1, '\0', sizeof(infos->downloading_file1));
        if(file2)
            snprintf(infos->downloading_file2, sizeof(infos->downloading_file2), "%s", file2);
        else
            memset(infos->downloading_file2, '\0', sizeof(infos->downloading_file2));
    }
    else{
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set files failed - invalid parameter: type (%d)", type);
    }

    write_file_webdav (infos);
}

void
provide_set_quota(quota_context value)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set quota failed - shared memory missing");
        return;
    }

    infos->storage_quota_avail  = value.storage_available;
    infos->storage_quota_used   = value.storage_used;
    infos->upload_quota_avail   = value.upload_avail;
    infos->upload_quota_used    = value.upload_used;
    infos->download_quota_avail = value.download_avail;
    infos->download_quota_used  = value.download_used;
    infos->traffic_quota_avail  = value.traffic_avail;
    infos->traffic_quota_used   = value.traffic_used;

	infos->storage_filecount          = value.storage_filecount;
	infos->storage_maxfilecount       = value.max_filecount;
	infos->storage_maxfilesperfolder  = value.max_filesperfolder;
	infos->storage_maxfilenamelength  = value.max_filenamelength;
	infos->storage_maxfilesize        = value.max_filesize;

	if(debug) {
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "quota settings: "
			"st_av: %llu, st_us: %llu, up_av: %llu, up_us: %llu, dw_av: %llu, dw_us: %llu, tr_av: %llu, tr_us: %llu",
			value.storage_available, value.storage_used,
			value.upload_avail, value.upload_used, value.download_avail,
			value.download_used, value.traffic_avail, value.traffic_used);
	}

	write_file_webdav (infos);
}

void
provide_get_quota(quota_context *quotas)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "get quota failed - shared memory missing");
        return;
    }

	quotas->storage_available  = infos->storage_quota_avail;
	quotas->storage_used       = infos->storage_quota_used;
	quotas->upload_avail       = infos->upload_quota_avail;
	quotas->upload_used        = infos->upload_quota_used;
	quotas->download_avail     = infos->download_quota_avail;
	quotas->download_used      = infos->download_quota_used;
	quotas->traffic_avail      = infos->traffic_quota_avail;
	quotas->traffic_used       = infos->traffic_quota_used;

	quotas->storage_filecount  = infos->storage_filecount;
	quotas->max_filecount      = infos->storage_maxfilecount;
	quotas->max_filesperfolder = infos->storage_maxfilesperfolder;
	quotas->max_filenamelength = infos->storage_maxfilenamelength;
	quotas->max_filesize       = infos->storage_maxfilesize;
}

void
provide_set_string(int type, const char* value)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set string failed - shared memory missing");
        return;
    }

    if(type == CONNECTION_STATE){
        if(!value)
            infos->connection_state = 0;
        else{
            if(!strcmp(value, "connected"))
                infos->connection_state = 1;
            else if(!strcmp(value, "disconnected"))
                infos->connection_state = 0;
            else if(strstr(value, "Could not resolve hostname") || strstr(value, "server temporarily unreachable"))
                infos->connection_state = 2;
            else if(strstr(value, "Could not authenticate to server") || strstr(value, "Authorization Required")
                 || strstr(value, "401") /* 1&1 Server z.B. gmx.de, web.de */)
                infos->connection_state = 3;
            else if(strstr(value, "invalid URL"))
                infos->connection_state = 4;
            else if(strstr(value, "Traffic limit reached"))
                infos->connection_state = 10;
            else if(strstr(value, "403")) // Forbidden
                infos->connection_state = 8;  // unknown error
            else
                //5 : readonly for usb activated
                //6 : no usb-mountpoint available for cache
                //7 : missing online connection
                infos->connection_state = 8; //unknown connection error
        }
        write_file_webdav_connection_state (infos);
    }
}


void print_speed(void)
{
    struct trans_speed* tmp;
    int cnt = 0;
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "--uploads");
    for(tmp = upload_speed_values; tmp; tmp = tmp->next){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "pos: %d-", cnt);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "start_time: %lu", tmp->start_time);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "bytes: %llu", tmp->bytes);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "seconds: %f", tmp->seconds);
        cnt++;
    }
    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "--downloads");
    cnt = 0;
    for(tmp = download_speed_values; tmp; tmp = tmp->next){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "pos: %d-", cnt);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "start_time: %lu", tmp->start_time);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "bytes: %llu", tmp->bytes);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "seconds: %f", tmp->seconds);
        cnt++;
    }
}


void clear_speed_values(int type)
{
    struct trans_speed* p = 0;
    struct trans_speed**pp = 0;
    struct trans_speed** speed_values = 0;
    if(type == TRANSFER_UPLOAD)
        speed_values = &upload_speed_values;
    else
        speed_values = &download_speed_values;

    for(pp = speed_values; *pp;){
        if((*pp)->start_time < time(NULL)-(TRANS_SPEED_INTERVAL*TRANS_SPEED_SLOTS)){
            p = *pp;
            *pp = (*pp)->next;
            free(p);
        }
        else
            pp = &(*pp)->next;

    }
}


void delete_speed_values(int type)
{
    struct trans_speed* p = 0;
    struct trans_speed**pp = 0;
    struct trans_speed** speed_values = 0;
    if(type == TRANSFER_UPLOAD)
        speed_values = &upload_speed_values;
    else
        speed_values = &download_speed_values;

    for(pp = speed_values; *pp;){
        p = *pp;
        *pp = (*pp)->next;
        free(p);
    }
}


void
append_speed_value(int type, uint64_t bytes, double seconds)
{
    struct trans_speed** tmp;
    if(type == TRANSFER_UPLOAD)
        for(tmp = &upload_speed_values; *tmp; tmp = &((*tmp)->next));
    else
        for(tmp = &download_speed_values; *tmp; tmp = &((*tmp)->next));
    *tmp = (struct trans_speed*)calloc(1, sizeof(struct trans_speed));
    if(!*tmp)
        return;
    (*tmp)->start_time  = time(NULL);
    (*tmp)->bytes       = bytes;
    (*tmp)->seconds     = seconds;
}


void
provide_set_transfer_speed(int type, uint64_t bytes, uint64_t duration)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "set transfer speed failed - shared memory missing");
        return;
    }

    double seconds = (double) duration / 1000000.0;

    if((type == TRANSFER_UPLOAD && !upload_speed_values) || (type == TRANSFER_DOWNLOAD && !download_speed_values)){
        //no time slot available
        append_speed_value(type, bytes, seconds);
    }
    else{
        struct trans_speed* last;
        if(type == TRANSFER_UPLOAD)
            for(last = upload_speed_values; last->next; last = last->next);
        else
            for(last = download_speed_values; last->next; last = last->next);
        //update last time slot
        if(time(NULL) < last->start_time+TRANS_SPEED_INTERVAL){
            last->bytes   += bytes;
            last->seconds += seconds;
        }
        else{
            //last time slot too old, append a new one
            //and remove expired slots
            append_speed_value(type, bytes, seconds);
        }
    }

    write_file_webdav (infos);
}


void
calculate_avg_speed(int type)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "calculating avg speed failed - shared memory missing");
        return;
    }

    struct trans_speed* tmp;
    struct trans_speed* speed_values;
    double total_seconds = 0;
    uint64_t total_bytes = 0;
    uint64_t bytes_per_seconds = 0;

    if(type == TRANSFER_UPLOAD)
        speed_values = upload_speed_values;
    else
        speed_values = download_speed_values;

    for(tmp = speed_values; tmp; tmp = tmp->next){
        if(tmp->bytes > 0 && tmp->seconds > 0){
            total_seconds += tmp->seconds;
            total_bytes += tmp->bytes;
        }
    }

    if(total_bytes == 0)
        bytes_per_seconds = 0;
    else
        bytes_per_seconds = (uint64_t) (total_bytes / total_seconds);
    if(type == TRANSFER_UPLOAD)
        infos->avg_upload_speed = bytes_per_seconds;
    else
        infos->avg_download_speed = bytes_per_seconds;

    write_file_webdav (infos);
}


void
is_low_upload_speed(void)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "checking low upload speed failed - shared memory missing");
        return;
    }

    struct trans_speed* tmp;
    int slot_count = 0;
    int is_slow = 1;
    uint64_t bytes_per_seconds = 0;

    for(tmp = upload_speed_values; tmp; tmp = tmp->next){
        if(tmp->bytes > 0 && tmp->seconds > 0){
            slot_count++;
            bytes_per_seconds = (uint64_t) (tmp->bytes / tmp->seconds);
            if((bytes_per_seconds*8)/1024>64)
                //greater than 64 Kbit/s
                is_slow = 0;
        }
    }
    if(slot_count == TRANS_SPEED_SLOTS)
        //notify "is-slow", only if uploads are running in all time-slots
        infos->low_upload_speed = is_slow;
    else
        infos->low_upload_speed = 0;

    write_file_webdav (infos);
}


void
provide_update_avg_speed(void)
{
    clear_speed_values(TRANSFER_UPLOAD);
    clear_speed_values(TRANSFER_DOWNLOAD);
    calculate_avg_speed(TRANSFER_UPLOAD);
    calculate_avg_speed(TRANSFER_DOWNLOAD);
    is_low_upload_speed();
}


void
provide_clear_transfervalues(void)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "clearing transfervalues failed - shared memory missing");
        return;
    }

    infos->dirty_files          = 0;
    infos->running_uploads      = 0;
    infos->finished_uploads     = 0;
    infos->failed_uploads       = 0;
    infos->running_downloads    = 0;
    infos->finished_downloads   = 0;
    infos->failed_downloads     = 0;
    infos->avg_upload_speed     = 0;
    infos->avg_download_speed   = 0;
    infos->low_upload_speed     = 0;
    memset(infos->uploading_file1, '\0', sizeof(infos->uploading_file1));
    memset(infos->uploading_file2, '\0', sizeof(infos->uploading_file2));
    memset(infos->downloading_file1, '\0', sizeof(infos->downloading_file1));
    memset(infos->downloading_file2, '\0', sizeof(infos->downloading_file2));
    delete_speed_values(TRANSFER_UPLOAD);
    delete_speed_values(TRANSFER_DOWNLOAD);

    write_file_webdav (infos);
}


void
provide_update_alivetime(void)
{
    if(!infos){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "updating alivetime failed - shared memory missing");
        return;
    }
    infos->alive_time = time(NULL);
    write_file_webdav (infos);

}


static int write_file_webdav(struct webdav_file_infos * webdav_infos)
{
	FILE *file;
	int err = -3;

	if(!webdav_infos)
		return -1;

	if(!(file = fopen(WEBDAVINFO_FILEPATH "/" WEBDAVINFO_FILETMP, "w"))) {
		return -2;
	}

	if(fprintf(file, WEBDAVINFO_STR_IS_RUNNING              ":%u\n",  webdav_infos->is_running)      < 0) goto error;

	if(fprintf(file, WEBDAVINFO_STR_CACHE_STORAGE_AVAIL     ":%llu\n", (unsigned long long)webdav_infos->cache_storage_avail)         < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_CACHE_STORAGE_USED      ":%llu\n", (unsigned long long)webdav_infos->cache_storage_used)          < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_DIRTY_FILES             ":%u\n",                       webdav_infos->dirty_files)                 < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_RUNNING_UPLOADS         ":%u\n",                       webdav_infos->running_uploads)             < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_RUNNING_DONWLOADS       ":%u\n",                       webdav_infos->running_downloads)           < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_FINISHED_UPLOADS        ":%llu\n", (unsigned long long)webdav_infos->finished_uploads)            < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_FAILED_UPLOADS          ":%llu\n", (unsigned long long)webdav_infos->failed_uploads)              < 0)  goto error;

	if(fprintf(file, WEBDAVINFO_STR_SUM_FAILED_UPLOADS      ":%llu\n", (unsigned long long)webdav_infos->sum_failed_uploads)          < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_FINISHED_DOWNLOADS      ":%llu\n", (unsigned long long)webdav_infos->finished_downloads)          < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_FAILED_DOWNLOADS        ":%llu\n", (unsigned long long)webdav_infos->failed_downloads)            < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_STORAGE_QUOTA_AVAIL     ":%llu\n", (unsigned long long)webdav_infos->storage_quota_avail)         < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_STORAGE_QUOTA_USED      ":%llu\n", (unsigned long long)webdav_infos->storage_quota_used)          < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_STORAGE_QUOTA_FILECOUNT ":%u\n",                       webdav_infos->storage_filecount)           < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_STORAGE_MAXFILECOUNT    ":%u\n",                       webdav_infos->storage_maxfilecount)        < 0) goto error;

	if(fprintf(file, WEBDAVINFO_STR_STORAGE_MAXFILESPERFOLDER   ":%u\n",                   webdav_infos->storage_maxfilesperfolder)   < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_STORAGE_MAXFILENAMELENGTH   ":%u\n",                   webdav_infos->storage_maxfilenamelength)   < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_STORAGE_MAXFILESIZE     ":%llu\n", (unsigned long long)webdav_infos->storage_maxfilesize)         < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_UPLOAD_QUOTA_AVAIL      ":%llu\n", (unsigned long long)webdav_infos->upload_quota_avail)          < 0)  goto error;

	if(fprintf(file, WEBDAVINFO_STR_UPLOAD_QUOTA_USED       ":%llu\n", (unsigned long long)webdav_infos->upload_quota_used)           < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_DOWNLOAD_QUOTA_AVAIL    ":%llu\n", (unsigned long long)webdav_infos->download_quota_avail)        < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_DOWNLOAD_QUOTA_USED     ":%llu\n", (unsigned long long)webdav_infos->download_quota_used)         < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_TRAFFIC_QUOTA_AVAIL     ":%llu\n", (unsigned long long)webdav_infos->traffic_quota_avail)         < 0)  goto error;

	if(fprintf(file, WEBDAVINFO_STR_TRAFFIC_QUOTA_USED      ":%llu\n", (unsigned long long)webdav_infos->traffic_quota_used)          < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_AVG_UPLOAD_SPEED        ":%llu\n", (unsigned long long)webdav_infos->avg_upload_speed)            < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_AVM_DOWNSLOAD_SPEED     ":%llu\n", (unsigned long long)webdav_infos->avg_download_speed)          < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_LOW_UPLOAD_SPEED        ":%u\n",                       webdav_infos->low_upload_speed)            < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_SIZE_DOWNLOADS          ":%llu\n", (unsigned long long)webdav_infos->size_downloads)              < 0)  goto error;
	if(fprintf(file, WEBDAVINFO_STR_SIZE_UPLOADS            ":%llu\n", (unsigned long long)webdav_infos->size_uploads)                < 0)  goto error;

	if(fprintf(file, WEBDAVINFO_STR_UPLOADING_FILE1         ":%s\n",                       webdav_infos->uploading_file1)              < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_UPLOADING_FILE2         ":%s\n",                       webdav_infos->uploading_file2)              < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_DOWNLOADING_FILE1       ":%s\n",                       webdav_infos->downloading_file1)            < 0) goto error;
	if(fprintf(file, WEBDAVINFO_STR_DOWNLOADING_FILE2       ":%s\n",                       webdav_infos->downloading_file2)            < 0) goto error;

	if(fprintf(file, WEBDAVINFO_STR_ALIVE_TIME              ":%llu\n", (unsigned long long)webdav_infos->alive_time)                   < 0)  goto error;


	fclose(file);
	rename(WEBDAVINFO_FILEPATH "/" WEBDAVINFO_FILETMP, WEBDAVINFO_FILEPATH "/" WEBDAVINFO_FILE);
	return 0;
error:

	fclose(file);
	syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "write webdavinfo file: error %i occurred", err);
	return err;
}


static int write_file_webdav_connection_state(struct webdav_file_infos * webdav_infos)
{
	FILE *file;
	int err = 0;
	if(!webdav_infos)
		return -1;

	if(!(file = fopen(WEBDAVINFO_FILEPATH "/" WEBDAVINFO_FILETMP_CONNECTION_STATE, "w"))) {
		return -2;
	}

	if(fprintf(file, WEBDAVINFO_STR_CONNECTION_STATE       ":%u\n",  webdav_infos->connection_state)      < 0) err = -3;

	fclose(file);
	rename(WEBDAVINFO_FILEPATH "/" WEBDAVINFO_FILETMP_CONNECTION_STATE, WEBDAVINFO_FILEPATH "/" WEBDAVINFO_FILE_CONNECTION_STATE);

	return err;
}

//************************
// Funktionen fuers das lesen aus dem Shared-Memory

static void shm_lock(int shmid)
{
	// Lock shared memory while updating infos.
	if(shmctl(shmid, SHM_LOCK, NULL)==-1)
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "locking shared memory failed");
}

static void shm_unlock(int shmid)
{
	// Unlock shared memory after updating infos.
	if(shmctl(shmid, SHM_UNLOCK, NULL)==-1)
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "unlocking shared memory failed");
}

#define member_size(type, member) sizeof(((type *)0)->member)

/**
 * Read values from USB configuration.
 *
 * @param debug_val enable/disable debug output
 * @param [in,out] args configuration values
 *
 * @retval 0 on success
 * @retval -1 on failure
 */
int provide_get_usb_cfg_infos(int debug_val, dav_args *args)
{
	struct webdav_shm_usb_cfg_infos * usb_cfg_infos = 0;
	/* ID of the created shared memory. */
	int shmid = 0;

	if (!args) return -1;

	debug = debug_val;

	shmid = shmget(WEBDAV_SHM_ID, sizeof(struct webdav_shm_usb_cfg_infos), 0666);
	if(shmid == -1){
		//shared memory not available, create it
		shmid = shmget(WEBDAV_SHM_ID, sizeof(struct webdav_shm_usb_cfg_infos), IPC_CREAT | 0666);
		if(shmid == -1){
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "creating shared memory failed: %s", strerror(errno));
			return -1;
		}
		if(debug)
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "shared memory created");
	}
	void* shm = shmat(shmid, 0, 0);
	if ((int)shm == -1) {
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "attaching shared memory failed: %s", strerror(errno));
		return -1;
	}
	if(debug)
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "shared memory attached");

	usb_cfg_infos = (struct webdav_shm_usb_cfg_infos*) shm;

	shm_lock(shmid);
	/* critical region - start */
	if (args->username) free (args->username);
	args->username = strndup(usb_cfg_infos->username, USB_CFG_STRLEN);
	
	if (args->password) free (args->password);
	args->password = strndup(usb_cfg_infos->password, USB_CFG_STRLEN);
	/* critical region - end */
	shm_unlock(shmid);

	if(shmid > 0){
		//detaching shared memory
		if(shmdt((void *)usb_cfg_infos) == -1)
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "detaching shared memory failed");
		else if(debug)
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "shared memory detached");
	}

	return 0;
}


/**
* AVM  provide_system_no_sh
* Execute a programm with the given parameters.
* Safer replacement of system() without a shell.
*
* @param prg    programm to execute
* @param para1  parameter for the programm, set NULL if not used
* @param para2  parameter for the programm, set NULL if not used
* @param para3  parameter for the programm, set NULL if not used
*
* @retval  0  success
* @retval >0  child/execvp returned with an error (child exit with 1)
* @retval -1  failure
*             - if prg is NULL or empty
*             - on fork() failure
*             - waitpid() failed
* @retval -2  child/prg was killed by a signal
* @retval -3  child/prg was stopped by a signal (should not happen)
* @retval -4  child/prg was continued by a signal (should not happen)
*/
int provide_system_no_sh(const char *prg, const char * para1, const char * para2, const char * para3)
{
	char *argv[5];  // letztes Platz als Abschluss immer auf NULL setzen
	int status = -1;
	pid_t pid;

	if (!prg || !*prg)
		return -1;

	argv[0] = (char*) prg;
	argv[1] = (char*) para1;
	argv[2] = (char*) para2;
	argv[3] = (char*) para3;
	argv[4] = NULL;  // Wenn keine Argumente mehr kommen, Ende / nach dem letzten Parameter immer auf NULL setzten!
	pid = fork();
	if (pid < 0) {
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "fork() failed");
		return -1;
	}
	if (pid == 0) { /* child */
		execvp(argv[0], argv);   // return -1 bei Fehler, Grund in errno
		// hier nur bei einem Fehler, bei erfolgreicher Ausfuehrung von execvp kehrt er hier nicht zurueck, sondern ersetzt den Child-Prozess
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "provide_system_no_sh(\"%s\" \"%s\" \"%s\" \"%s\"): execvp() failed, errno %d", prg, para1?para1:"", para2?para2:"", para3?para3:"", errno);
		exit(1); // Fehler
	}
	/* parent */
	if (waitpid(pid, &status, 0) < 0) {  // wenn WNOHANG mit angegeben wird, kann der Return-Wert auch 0 sein
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "waitpid(%d) failed", pid);
		return -1;
	} else {
		if (WIFEXITED(status)) {
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "waitpid child exited, status=%d\n", WEXITSTATUS(status));
			return WEXITSTATUS(status); // child sauber beendet, Exit code zurueckgeben 0 OK, 1 Fehler beim execvp oder Fehlercode vom Programm
		} else if (WIFSIGNALED(status)) {
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "waitpid child killed by signal %d\n", WTERMSIG(status));
			return -2;
		} else if (WIFSTOPPED(status)) {  // nur moeglich wenn beim Aufruf von waitpid WUNTRACED angegeben wird
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "waitpid child stopped by signal %d\n", WSTOPSIG(status));
			return -3;
		} else if (WIFCONTINUED(status)) {
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "waitpid child continued\n");
			return -4;
		}
	}

	return -1;
}