/******************************************************************************!
* \file mp3server.c
* \author Sebastien Beaugrand
* \sa http://beaugrand.chez.com/
* \copyright CeCILL 2.1 Free Software license
******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include "mp3server.h"
#include "player.h"
#include "log.h"
#include "resources.h"
#include "html.h"
#include "common.h"
struct part_list* gPartRoot = NULL;
struct Buffer* gBuffer = NULL;
struct tm* gTmOfTheDay = NULL;
pid_t gClientPid = 0;
struct timeval gTempo = {
0, 0
};
unsigned int gNbWeights = 0;
int* gWeights = NULL;
/******************************************************************************!
* \fn mp3serverWaitMp3rootDir
******************************************************************************/
void mp3serverWaitMp3rootDir(const char* root)
{
char filename[LINE_SIZE];
strcpy(filename, root);
strcat(filename, "/mp3");
while (access(filename, R_OK) != 0) {
sleep(1);
}
}
/******************************************************************************!
* \fn mp3serverGetMp3rootDir
******************************************************************************/
const char* mp3serverGetMp3rootDir()
{
static char* root = NULL;
if (root == NULL) {
root = getenv("MP3DIR");
if (root == NULL) {
root = "/mnt/mp3";
}
mp3serverWaitMp3rootDir(root);
}
return root;
}
/******************************************************************************!
* \fn mp3serverWeightsInit
******************************************************************************/
void mp3serverWeightsInit()
{
char filename[LINE_SIZE];
char line[LINE_SIZE];
char* ptr;
FILE* fp;
FILE* buffFile;
unsigned int i;
gNbWeights = 0;
strcpy(filename, mp3serverGetMp3rootDir());
strcat(filename, "/.mp3weights");
fp = fopen(filename, "r");
if (fp == NULL) {
ERROR(".mp3weights not found");
return;
}
buffFile = bufferInit(gBuffer);
while (fgets(line, LINE_SIZE, fp) != NULL) {
ptr = strtok(line, " ");
for (; ptr != NULL; ++gNbWeights) {
fprintf(buffFile, "%d ", atoi(ptr));
ptr = strtok(NULL, " ");
}
}
fclose(fp);
DEBUG("nb weights = %u", gNbWeights);
DEBUG("weights =");
i = 0;
gWeights = malloc(gNbWeights * sizeof(int));
ptr = strtok(bufferGet(gBuffer), " ");
do {
gWeights[i] = atoi(ptr);
DEBUG(" %d", gWeights[i]);
++i;
} while ((ptr = strtok(NULL, " ")) != NULL);
}
/******************************************************************************!
* \fn mp3serverTmOfTheDay
******************************************************************************/
struct tm* mp3serverTmOfTheDay()
{
time_t tOfTheDay;
char dateOfTheDay[20];
tOfTheDay = time(NULL);
gTmOfTheDay = localtime(&tOfTheDay);
strftime(dateOfTheDay, sizeof(dateOfTheDay), "%F %T", gTmOfTheDay);
DEBUG("date = %s", dateOfTheDay);
return gTmOfTheDay;
}
/******************************************************************************!
* \fn mp3serverDeleteBlocList
******************************************************************************/
void mp3serverDeleteBlocList(struct bloc_list* bloc)
{
struct bloc_list* next;
unsigned int i;
while (bloc != NULL) {
for (i = 0; i < bloc->count; ++i) {
free(bloc->name[i]);
}
next = bloc->next;
free(bloc);
bloc = next;
}
}
/******************************************************************************!
* \fn mp3serverDeletePartList
******************************************************************************/
void mp3serverDeletePartList(struct part_list* part)
{
struct part_list* next;
while (part != NULL) {
free(part->name);
mp3serverDeleteBlocList(part->bloc);
next = part->next;
free(part);
part = next;
}
}
/******************************************************************************!
* \fn mp3serverQuit
******************************************************************************/
void mp3serverQuit(int status)
{
if (gBuffer != NULL) {
bufferQuit(gBuffer);
free(gBuffer);
}
if (gPartRoot != NULL) {
mp3serverDeletePartList(gPartRoot);
}
if (gWeights != NULL) {
free(gWeights);
}
playerQuit();
httpQuit();
exit(status);
}
/******************************************************************************!
* \fn controlC
******************************************************************************/
void controlC(int sig)
{
if (sig == SIGINT) {
if (gClientPid != 0) {
mp3serverSignalToClient(SIGINT);
}
mp3serverQuit(EXIT_SUCCESS);
}
}
/******************************************************************************!
* \fn mp3serverFillAlbumsForArtist
******************************************************************************/
struct bloc_list*
mp3serverFillAlbumsForArtist(struct part_list* partRoot, const char* artist)
{
unsigned int j;
struct part_list* part_elem;
struct bloc_list* bloc_elem = NULL;
struct bloc_list* bloc_artist_root = NULL;
struct bloc_list* bloc_artist_elem = NULL;
char* str;
int len;
len = strlen(artist);
part_elem = partRoot;
while (part_elem != NULL) {
bloc_elem = part_elem->bloc;
while (bloc_elem != NULL) {
for (j = 0; j < bloc_elem->count; ++j) {
if (strncmp(bloc_elem->name[j], artist, len) == 0) {
if (bloc_artist_elem == NULL) {
bloc_artist_elem = malloc(sizeof(struct bloc_list));
bloc_artist_elem->count = 0;
bloc_artist_elem->next = NULL;
bloc_artist_root = bloc_artist_elem;
}
if (bloc_artist_elem->count == BLOC_SIZE) {
bloc_artist_elem->next =
malloc(sizeof(struct bloc_list));
bloc_artist_elem = bloc_artist_elem->next;
bloc_artist_elem->count = 0;
bloc_artist_elem->next = NULL;
}
str = malloc(strlen(part_elem->name) +
strlen(bloc_elem->name[j]) + 13);
strcpy(str, " ");
strcat(str, part_elem->name);
strcat(str, "/");
strcat(str, bloc_elem->name[j]);
bloc_artist_elem->name[bloc_artist_elem->count] = str;
bloc_artist_elem->count++;
}
}
bloc_elem = bloc_elem->next;
}
part_elem = part_elem->next;
}
return bloc_artist_root;
}
/******************************************************************************!
* \fn mp3serverArtist
******************************************************************************/
struct bloc_list*
mp3serverArtist(struct part_list* partRoot, const char** newAlbum)
{
static char album[LINE_SIZE];
char line[LINE_SIZE];
char artist[LINE_SIZE];
int i;
if (gTmOfTheDay == NULL) {
mp3serverTmOfTheDay();
}
if (*newAlbum == NULL) {
if (logGetLastAlbum(&line) == 0) {
ERROR("logGetLastAlbum == 0");
return NULL;
}
} else {
strcpy(line, *newAlbum);
}
for (i = strlen(line) - 1; i >= 0 && line[i] != '/'; --i) {
;
}
if (line[i] != '/') {
ERROR("/ not found");
return NULL;
}
strcpy(album, line + i);
*newAlbum = album + 1;
for (i = 0; i < LINE_SIZE - 2 &&
(album[i + 1] != '-' || album[i + 2] != ' ' || album[i] != ' ');
++i) {
artist[i] = album[i + 1];
}
if (i >= LINE_SIZE - 2 || album[i + 1] != '-') {
DEBUG("- not found");
return NULL;
}
artist[i] = '-';
artist[i + 1] = ' ';
artist[i + 2] = '\0';
return logFillDatesFromLogs(mp3serverFillAlbumsForArtist(partRoot, artist),
artist, gTmOfTheDay);
}
/******************************************************************************!
* \fn mp3serverGetArtist
******************************************************************************/
const char* mp3serverGetArtist(const char* a)
{
static char artist[LINE_SIZE];
struct part_list* part_elem;
struct bloc_list* bloc_elem = NULL;
unsigned int i;
const char* a1;
const char* a2;
part_elem = gPartRoot;
while (part_elem != NULL) {
bloc_elem = part_elem->bloc;
while (bloc_elem != NULL) {
for (i = 0; i < bloc_elem->count; ++i) {
a1 = bloc_elem->name[i];
a2 = a;
while (*a2 != '\0') {
if (*a1 != *a2 &&
*a1 != *a2 + 32) {
break;
}
++a1;
while (*a1 == ' ' ||
*a1 == 'x' ||
*a1 == 'X') {
++a1;
}
++a2;
}
if (*a2 == '\0') {
sprintf(artist, "%s/%s",
part_elem->name, bloc_elem->name[i]);
return artist;
}
}
bloc_elem = bloc_elem->next;
}
part_elem = part_elem->next;
}
*artist = '\0';
return artist;
}
/******************************************************************************!
* \fn mp3serverGetRelativeDate
******************************************************************************/
const char* mp3serverGetRelativeDate(const char* ymd)
{
static char dateR[3];
int y;
int m;
int d;
int dInit;
if (ymd == NULL) {
return NULL;
}
if (gTmOfTheDay == NULL) {
mp3serverTmOfTheDay();
}
if (sscanf(ymd, "%4d-%2d-%2d", &y, &m, &d) != 3) {
return NULL;
}
y = gTmOfTheDay->tm_year + 1900 - y;
m = gTmOfTheDay->tm_mon + 1 - m;
d = gTmOfTheDay->tm_mday - d;
DEBUG("y = %d, m = %d, d = %d", y, m, d);
if (d < 0) {
d += 31;
m -= 1;
}
if (m < 0) {
m += 12;
y -= 1;
}
d += m * 31 + y * 372;
DEBUG("d = %d", d);
if (d < 0) { // When DS1302 is frozen
d = 0;
}
if (d == 0) {
mp3serverTmOfTheDay();
}
dInit = d;
if (d > 9) {
d /= 7;
if (d > 9) {
d = dInit / 31;
if (d > 9) {
d /= 12;
dateR[1] = 'A';
} else {
dateR[1] = 'M';
}
} else {
dateR[1] = 'S';
}
} else {
dateR[1] = 'J';
}
dateR[0] = '0' + d;
dateR[2] = '\0';
return dateR;
}
/******************************************************************************!
* \fn mp3serverDirFilter
******************************************************************************/
int mp3serverDirFilter(const struct dirent* d)
{
if (d->d_name[0] == '.') {
return 0;
}
if (d->d_type != DT_UNKNOWN &&
d->d_type != DT_DIR) {
return 0;
}
return 1;
}
/******************************************************************************!
* \fn mp3serverGetHtmlDir
******************************************************************************/
struct Buffer* mp3serverGetHtmlDir(const char* dir)
{
struct dirent** filelist = NULL;
char fulldir[LINE_SIZE];
int ndirs;
int i;
int max;
FILE* buffFile = bufferInit(gBuffer);
fseek(buffFile, httpGetHttpOkSize(), SEEK_SET);
fprintf(buffFile, "%s", htmlGetBegin());
strcpy(fulldir, mp3serverGetMp3rootDir());
strcat(fulldir, "/mp3");
if (dir[0] != '\0') {
strcat(fulldir, "/");
strcat(fulldir, dir);
}
fprintf(buffFile, "%s", htmlGetTableBegin());
ndirs = scandir(fulldir, &filelist, mp3serverDirFilter, alphasort);
if (dir[0] == '\0') {
max = ndirs;
} else {
max = ndirs / DIR_PAGE_NB;
}
for (i = 0; i < ndirs; ++i) {
if (strstr(filelist[i]->d_name, "tmp") != NULL) {
continue;
}
if (i >= max) {
fprintf(buffFile, "%s", htmlGetTableNewTd());
max += ndirs / DIR_PAGE_NB;
if (max >= ndirs - (DIR_PAGE_NB - 1)) {
max = ndirs;
}
}
if (dir[0] == '\0') {
fprintf(buffFile, "%s", htmlGetHrefDir());
} else {
fprintf(buffFile, "%s", htmlGetHrefAlbum());
fprintf(buffFile, "%s", dir);
}
fprintf(buffFile, "%s", filelist[i]->d_name);
fprintf(buffFile, "%s", "/?format=html\">");
fprintf(buffFile, "%s", filelist[i]->d_name);
fprintf(buffFile, "%s", "</a><br/>");
}
if (filelist != NULL) {
for (i = 0; i < ndirs; ++i) {
free(filelist[i]);
}
free(filelist);
}
fprintf(buffFile, "%s", htmlGetTableEnd());
fprintf(buffFile, "%s", htmlGetEnd());
return gBuffer;
}
/******************************************************************************!
* \fn mp3serverMp3info
******************************************************************************/
struct Buffer* mp3serverMp3info(const char* newAlbum, enum tFormat format)
{
char line[LINE_SIZE << 1];
char curAlbum[LINE_SIZE];
unsigned int j;
struct bloc_list* bloc_elem = NULL;
struct bloc_list* bloc_artist_root = NULL;
char* str;
const char* dateR;
const char* album;
FILE* buffFile = bufferInit(gBuffer);
if (format == HTML) {
fseek(buffFile, httpGetHttpOkSize(), SEEK_SET);
fprintf(buffFile, "%s", htmlGetBegin());
}
*curAlbum = '\0';
album = newAlbum;
bloc_artist_root = mp3serverArtist(gPartRoot, &album);
if (bloc_artist_root == NULL) {
return gBuffer;
}
bloc_elem = bloc_artist_root;
while (bloc_elem != NULL) {
for (j = 0; j < bloc_elem->count; ++j) {
str = bloc_elem->name[j];
dateR = mp3serverGetRelativeDate(str);
if (dateR != NULL) {
strcpy(line, dateR);
strcat(line, " ");
} else {
strcpy(line, " ");
}
strncpy(line + 3, str, 11);
if (strlen(str) >= strlen(album) + 11 &&
strcmp(str + strlen(str) - strlen(album), album) == 0) {
if (format == HTML) {
sprintf(line + 3 + 11,
"%s%s/?format=html\""
" style=color:#ffffff>%s</a><br/>",
htmlGetHrefAlbum(),
str + 11,
str + 11);
} else {
sprintf(line + 3 + 11, "%s\n", str + 11);
strcpy(curAlbum, line);
curAlbum[strlen(curAlbum) - 1] = '\0';
}
} else {
if (format == HTML) {
sprintf(line + 3 + 11,
"%s%s/?format=html\">%s</a><br/>",
htmlGetHrefAlbum(),
str + 11,
str + 11);
} else {
sprintf(line + 3 + 11, "%s\n", str + 11);
}
}
fprintf(buffFile, "%s", line);
}
bloc_elem = bloc_elem->next;
}
mp3serverDeleteBlocList(bloc_artist_root);
if (format == HTML) {
fprintf(buffFile, "%s", htmlGetEnd());
} else {
fprintf(buffFile, "\n%s", curAlbum);
}
return gBuffer;
}
/******************************************************************************!
* \fn mp3serverReadMp3List
******************************************************************************/
void mp3serverReadMp3List()
{
FILE* fd;
char listFilename[LINE_SIZE];
char line[LINE_SIZE];
char prev[LINE_SIZE];
char* part;
char* title;
unsigned int nbParts = 0;
struct part_list* part_elem = NULL;
struct part_list* part_prev = NULL;
struct bloc_list* bloc_elem = NULL;
struct bloc_list* bloc_prev = NULL;
strcpy(listFilename, mp3serverGetMp3rootDir());
strcat(listFilename, "/mp3/mp3.list");
fd = fopen(listFilename, "r");
if (fd == NULL) {
ERROR("fopen %s", listFilename);
mp3serverQuit(EXIT_FAILURE);
}
prev[0] = '\0';
if (gPartRoot != NULL) {
free(gPartRoot);
gPartRoot = NULL;
}
while (fgets(line, LINE_SIZE, fd) != NULL) {
if (strstr(line, "tmp") != NULL) {
continue;
}
if ((part = strtok(line, "/")) == NULL) {
continue;
}
if ((title = strtok(NULL, "/")) == NULL) {
continue;
}
if (strcmp(part, prev) != 0) {
nbParts++;
if (gNbWeights > 0 && nbParts > gNbWeights) {
ERROR("nbParts > gNbWeights");
break;
}
strcpy(prev, part);
DEBUG("%s", prev);
part_elem = malloc(sizeof(struct part_list));
part_elem->name = malloc(strlen(prev) + 1);
strcpy(part_elem->name, prev);
part_elem->count = 0;
part_elem->bloc = NULL;
part_elem->next = NULL;
if (gPartRoot == NULL) {
gPartRoot = part_elem;
} else {
part_prev->next = part_elem;
}
part_prev = part_elem;
bloc_prev = NULL;
}
if (bloc_prev == NULL) {
bloc_elem = malloc(sizeof(struct bloc_list));
bloc_elem->count = 0;
bloc_elem->next = NULL;
bloc_prev = bloc_elem;
part_elem->bloc = bloc_elem;
} else if (bloc_prev->count == BLOC_SIZE) {
DEBUG("BLOC_SIZE");
bloc_elem = malloc(sizeof(struct bloc_list));
bloc_elem->count = 0;
bloc_elem->next = NULL;
bloc_prev->next = bloc_elem;
bloc_prev = bloc_elem;
}
bloc_elem->name[bloc_elem->count] = malloc(strlen(title) + 1);
strcpy(bloc_elem->name[bloc_elem->count], title);
bloc_elem->count++;
part_elem->count++;
}
fclose(fd);
if (gNbWeights == 0) {
gNbWeights = nbParts;
gWeights = malloc(gNbWeights * sizeof(int));
for (nbParts = 0; nbParts < gNbWeights; ++nbParts) {
gWeights[nbParts] = 1;
}
}
}
/******************************************************************************!
* \fn mp3serverStartAlbum
******************************************************************************/
void mp3serverStartAlbum(const char* album)
{
char m3u[LINE_SIZE];
logMp3log(album);
strcpy(m3u, "file://");
strcat(m3u, mp3serverGetMp3rootDir());
strcat(m3u, "/mp3/");
strcat(m3u, album);
strcat(m3u, "/00.m3u");
DEBUG("m3u = %s", m3u);
playerM3u(m3u);
mp3serverStopTempo();
}
/******************************************************************************!
* \fn mp3serverReadAlbum
******************************************************************************/
void mp3serverReadAlbum()
{
char album[LINE_SIZE];
if (logGetLastAlbum(&album) == 0) {
return;
}
mp3serverStartAlbum(album + 11);
}
/******************************************************************************!
* \fn mp3serverGetRandomNumber
******************************************************************************/
int mp3serverGetRandomNumber(unsigned int min, unsigned int max)
{
static int r = -1;
char line[LINE_SIZE];
FILE* fp = NULL;
int i;
unsigned int seed;
strcpy(line, mp3serverGetMp3rootDir());
strcat(line, "/.mp3rand");
if (r == -1) {
fp = fopen(line, "r+");
if (fp != NULL) {
fseek(fp, -LINE_SIZE, SEEK_END);
i = fread(line, 1, LINE_SIZE, fp);
if (i > 1) {
line[i - 1] = '\0';
for (i -= 2; i >= 0 && line[i] != '\n'; --i) {
;
}
if (i >= 0) {
seed = atoi(line + (i + 1));
srand(seed);
r = rand();
} else {
ERROR("i < 0");
seed = time(NULL);
srand(seed);
r = seed;
}
} else {
ERROR("fread");
seed = time(NULL);
srand(seed);
r = seed;
}
} else {
ERROR("fopen");
seed = time(NULL);
srand(seed);
r = seed;
}
DEBUG("seed = %u", seed);
} else {
r = rand();
}
i = r / ((RAND_MAX / (max - min + 1)) + 1) + min;
if (fp != NULL) {
if (fseek(fp, 0, SEEK_END) == -1) {
ERROR("fseek");
}
} else {
fp = fopen(line, "a");
if (fp == NULL) {
ERROR("fopen");
}
}
if (fp != NULL) {
fprintf(fp, "%d %u %u %d\n", r, min, max, i);
fclose(fp);
}
return i;
}
/******************************************************************************!
* \fn mp3serverMp3rand
******************************************************************************/
struct Buffer* mp3serverMp3rand(enum tFormat format)
{
char album[LINE_SIZE];
struct part_list* part_elem = gPartRoot;
struct bloc_list* bloc_elem = NULL;
int part;
int max;
int r;
unsigned int i;
FILE* buffFile;
if (gPartRoot == NULL) {
ERROR("gPartRoot == NULL");
buffFile = bufferInit(gBuffer);
fseek(buffFile, httpGetHttpOkSize(), SEEK_SET);
return gBuffer;
}
max = 0;
for (i = 0; i < gNbWeights; i++) {
max += gWeights[i];
}
r = mp3serverGetRandomNumber(0, max - 1);
DEBUG("max = %d rand = %d", max, r);
part = 1;
max = 0;
for (i = 0; i < gNbWeights; ++i) {
max += gWeights[i];
if (r < max) {
break;
}
part++;
part_elem = part_elem->next;
if (part_elem == NULL) {
ERROR("part_elem == NULL");
buffFile = bufferInit(gBuffer);
fseek(buffFile, httpGetHttpOkSize(), SEEK_SET);
return gBuffer;
}
}
DEBUG("name = %s", part_elem->name);
max = part_elem->count;
r = mp3serverGetRandomNumber(0, max - 1);
DEBUG("max = %d rand = %d", max, r);
bloc_elem = part_elem->bloc;
for (i = (r >> 4); i > 0; i--) {
bloc_elem = bloc_elem->next;
if (bloc_elem == NULL) {
ERROR("bloc_elem == NULL");
buffFile = bufferInit(gBuffer);
fseek(buffFile, httpGetHttpOkSize(), SEEK_SET);
return gBuffer;
}
}
DEBUG("ptr = %p", bloc_elem->name);
DEBUG("name = %s", bloc_elem->name[r & 0x0F]);
strcpy(album, part_elem->name);
strcat(album, "/");
strcat(album, bloc_elem->name[r & 0x0F]);
logMp3log(album);
mp3serverStartTempo();
return mp3serverMp3info(album, format);
}
/******************************************************************************!
* \fn mp3serverSavePlaytime
******************************************************************************/
void mp3serverSavePlaytime(int32_t playtime)
{
char timefile[LINE_SIZE];
FILE* fp;
strcpy(timefile, mp3serverGetMp3rootDir());
strcat(timefile, "/.mp3time");
fp = fopen(timefile, "w");
if (fp != NULL) {
fprintf(fp, "%d", playtime);
fclose(fp);
}
}
/******************************************************************************!
* \fn mp3serverSignalToClient
******************************************************************************/
void mp3serverSignalToClient(int sig)
{
# if defined(__arm__) || defined(__aarch64__)
char cmd[32];
# endif
if (gClientPid != 0) {
# if defined(__arm__) || defined(__aarch64__)
sprintf(cmd, "sudo kill -%d %u", sig, gClientPid);
if (system(cmd) != EXIT_SUCCESS) {
}
# else
kill(gClientPid, sig);
# endif
}
}
/******************************************************************************!
* \fn mp3serverStartTempo
******************************************************************************/
void mp3serverStartTempo()
{
gettimeofday(&gTempo, NULL);
}
/******************************************************************************!
* \fn mp3serverStopTempo
******************************************************************************/
int mp3serverStopTempo()
{
if (gTempo.tv_sec != 0) {
gTempo.tv_sec = 0;
return 1;
}
return 0;
}
/******************************************************************************!
* \fn mp3serverSetClientPid
******************************************************************************/
void mp3serverSetClientPid(pid_t pid)
{
gClientPid = pid;
}
/******************************************************************************!
* \fn main
******************************************************************************/
int main()
{
struct timeval tv;
struct rlimit rlim;
mp3serverGetMp3rootDir();
# ifdef RPI
if (system("sudo /etc/init.d/fake-hwclock start >/dev/null") !=
EXIT_SUCCESS) {
ERROR("system /etc/init.d/fake-hwclock start != 0");
}
# endif
getrlimit(RLIMIT_CORE, &rlim);
DEBUG("rlimit_core = %lu(cur) %lu(max)", rlim.rlim_cur, rlim.rlim_max);
rlim.rlim_cur = RLIM_INFINITY;
rlim.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &rlim);
getrlimit(RLIMIT_CORE, &rlim);
DEBUG("rlimit_core = %lu(cur) %lu(max)", rlim.rlim_cur, rlim.rlim_max);
if (playerInit() != 0) {
return EXIT_FAILURE;
}
gBuffer = bufferNew();
mp3serverWeightsInit();
mp3serverReadMp3List();
createResources();
httpRunServer(gBuffer);
playerResume();
if (signal(SIGINT, controlC) == SIG_ERR) {
ERROR("signal ctrl-c");
return EXIT_FAILURE;
}
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
ERROR("signal sigpipe");
return EXIT_FAILURE;
}
for (;;) {
nanoSleep(100000000);
httpRunServer(gBuffer);
if (gTempo.tv_sec != 0) {
gettimeofday(&tv, NULL);
if (tv.tv_sec > gTempo.tv_sec + 30) {
gTempo.tv_sec = 0;
logUnreadAlbum();
if (gClientPid != 0) {
mp3serverSignalToClient(SIGUSR1);
}
}
}
}
return EXIT_SUCCESS;
}