Fix: valgrind memory still reachable errors in read_statefiles

Fix: thread termination of mympd_api_thread
This commit is contained in:
jcorporation 2019-01-25 01:08:38 +01:00
parent f21e96444f
commit 79900f84e1
6 changed files with 262 additions and 242 deletions

View File

@ -33,55 +33,6 @@
#include "list.h"
#include "global.h"
bool state_file_read(t_config *config, const char *name, char *value) {
char cfg_file[400];
char *line = NULL;
size_t n = 0;
ssize_t read;
if (!validate_string(name))
return false;
snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name);
FILE *fp = fopen(cfg_file, "r");
if (fp == NULL) {
printf("Error opening %s\n", cfg_file);
return false;
}
read = getline(&line, &n, fp);
snprintf(value, 400, "%s", line);
LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, value);
fclose(fp);
free(line);
if (read > 0)
return true;
else
return false;
}
bool state_file_write(t_config *config, const char *name, const char *value) {
char tmp_file[400];
char cfg_file[400];
if (!validate_string(name))
return false;
snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name);
snprintf(tmp_file, 400, "%s/tmp/%s", config->varlibdir, name);
FILE *fp = fopen(tmp_file, "w");
if (fp == NULL) {
printf("Error opening %s\n", tmp_file);
return false;
}
fprintf(fp, "%s", value);
fclose(fp);
if (rename(tmp_file, cfg_file) == -1) {
printf("Error renaming file from %s to %s\n", tmp_file, cfg_file);
return false;
}
return true;
}
bool testdir(const char *name, const char *dirname) {
DIR* dir = opendir(dirname);
if (dir) {

View File

@ -158,33 +158,33 @@ typedef struct t_work_result {
//myMPD configuration
typedef struct t_config {
long mpdport;
const char *mpdhost;
const char *mpdpass;
const char *webport;
char *mpdhost;
char *mpdpass;
char *webport;
bool ssl;
const char *sslport;
const char *sslcert;
const char *sslkey;
const char *user;
char *sslport;
char *sslcert;
char *sslkey;
char *user;
bool coverimage;
const char *coverimagename;
char *coverimagename;
long coverimagesize;
bool stickers;
bool mixramp;
const char *taglist;
const char *searchtaglist;
const char *browsetaglist;
char *taglist;
char *searchtaglist;
char *browsetaglist;
bool smartpls;
const char *varlibdir;
const char *etcdir;
char *varlibdir;
char *etcdir;
unsigned long max_elements_per_page;
bool syscmds;
bool localplayer;
long streamport;
const char *streamurl;
char *streamurl;
unsigned long last_played_count;
long loglevel;
const char *backgroundcolor;
char *backgroundcolor;
} t_config;
//global functions
@ -194,6 +194,4 @@ bool validate_string(const char *data);
int copy_string(char * const dest, char const * const src, size_t const dst_len, size_t const src_len);
int replacechar(char *str, const char orig, const char rep);
enum mympd_cmd_ids get_cmd_id(const char *cmd);
bool state_file_read(t_config *config, const char *name, char *value);
bool state_file_write(t_config *config, const char *name, const char *value);
#endif

View File

@ -47,6 +47,8 @@
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler); // Reinstantiate signal handler
s_signal_received = sig_num;
//Wakeup mympd_api_loop
pthread_cond_signal(&mympd_api_queue->wakeup);
}
static int inihandler(void *user, const char *section, const char *name, const char* value) {
@ -55,61 +57,78 @@ static int inihandler(void *user, const char *section, const char *name, const c
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("mpd", "mpdhost"))
if (MATCH("mpd", "mpdhost")) {
free(p_config->mpdhost);
p_config->mpdhost = strdup(value);
else if (MATCH("mpd", "mpdport"))
}
else if (MATCH("mpd", "mpdport")) {
p_config->mpdport = strtol(value, &crap, 10);
else if (MATCH("mpd", "mpdpass"))
}
else if (MATCH("mpd", "mpdpass")) {
free(p_config->mpdpass);
p_config->mpdpass = strdup(value);
else if (MATCH("mpd", "streamport"))
}
else if (MATCH("mpd", "streamport")) {
p_config->streamport = strtol(value, &crap, 10);
else if (MATCH("webserver", "webport"))
}
else if (MATCH("webserver", "webport")) {
free(p_config->webport);
p_config->webport = strdup(value);
else if (MATCH("webserver", "ssl"))
if (strcmp(value, "true") == 0)
p_config->ssl = true;
else
p_config->ssl = false;
else if (MATCH("webserver", "sslport"))
}
else if (MATCH("webserver", "ssl")) {
p_config->ssl = strcmp(value, "true") == 0 ? true : false;
}
else if (MATCH("webserver", "sslport")) {
free(p_config->sslport);
p_config->sslport = strdup(value);
else if (MATCH("webserver", "sslcert"))
}
else if (MATCH("webserver", "sslcert")) {
free(p_config->sslcert);
p_config->sslcert = strdup(value);
else if (MATCH("webserver", "sslkey"))
}
else if (MATCH("webserver", "sslkey")) {
free(p_config->sslkey);
p_config->sslkey = strdup(value);
else if (MATCH("mympd", "user"))
}
else if (MATCH("mympd", "user")) {
free(p_config->user);
p_config->user = strdup(value);
else if (MATCH("mympd", "coverimage"))
if (strcmp(value, "true") == 0)
p_config->coverimage = true;
else
p_config->coverimage = false;
else if (MATCH("mympd", "coverimagename"))
}
else if (MATCH("mympd", "coverimage")) {
p_config->coverimage = strcmp(value, "true") == 0 ? true : false;
}
else if (MATCH("mympd", "coverimagename")) {
free(p_config->coverimagename);
p_config->coverimagename = strdup(value);
else if (MATCH("mympd", "coverimagesize"))
}
else if (MATCH("mympd", "coverimagesize")) {
p_config->coverimagesize = strtol(value, &crap, 10);
else if (MATCH("mympd", "varlibdir"))
}
else if (MATCH("mympd", "varlibdir")) {
free(p_config->varlibdir);
p_config->varlibdir = strdup(value);
else if (MATCH("mympd", "stickers"))
if (strcmp(value, "true") == 0)
p_config->stickers = true;
else
p_config->stickers = false;
else if (MATCH("mympd", "smartpls"))
if (strcmp(value, "true") == 0)
p_config->smartpls = true;
else
p_config->smartpls = false;
else if (MATCH("mympd", "mixramp"))
if (strcmp(value, "true") == 0)
p_config->mixramp = true;
else
p_config->mixramp = false;
else if (MATCH("mympd", "taglist"))
}
else if (MATCH("mympd", "stickers")) {
p_config->stickers = strcmp(value, "true") == 0 ? true : false;
}
else if (MATCH("mympd", "smartpls")) {
p_config->smartpls = strcmp(value, "true") == 0 ? true : false;
}
else if (MATCH("mympd", "mixramp")) {
p_config->mixramp = strcmp(value, "true") == 0 ? true : false;
}
else if (MATCH("mympd", "taglist")) {
free(p_config->taglist);
p_config->taglist = strdup(value);
else if (MATCH("mympd", "searchtaglist"))
p_config->searchtaglist = strdup(value);
else if (MATCH("mympd", "browsetaglist"))
}
else if (MATCH("mympd", "searchtaglist")) {
free(p_config->searchtaglist);
p_config->searchtaglist = strdup(value);
}
else if (MATCH("mympd", "browsetaglist")) {
free(p_config->browsetaglist);
p_config->browsetaglist = strdup(value);
}
else if (MATCH("mympd", "max_elements_per_page")) {
p_config->max_elements_per_page = strtol(value, &crap, 10);
if (p_config->max_elements_per_page > MAX_ELEMENTS_PER_PAGE) {
@ -117,27 +136,29 @@ static int inihandler(void *user, const char *section, const char *name, const c
p_config->max_elements_per_page = MAX_ELEMENTS_PER_PAGE;
}
}
else if (MATCH("mympd", "syscmds"))
if (strcmp(value, "true") == 0)
p_config->syscmds = true;
else
p_config->syscmds = false;
else if (MATCH("mympd", "localplayer"))
if (strcmp(value, "true") == 0)
p_config->localplayer = true;
else
p_config->localplayer = false;
else if (MATCH("mympd", "streamurl"))
else if (MATCH("mympd", "syscmds")) {
p_config->syscmds = strcmp(value, "true") == 0 ? true : false;
}
else if (MATCH("mympd", "localplayer")) {
p_config->localplayer = strcmp(value, "true") == 0 ? true : false;
}
else if (MATCH("mympd", "streamurl")) {
free(p_config->streamurl);
p_config->streamurl = strdup(value);
else if (MATCH("mympd", "last_played_count"))
}
else if (MATCH("mympd", "last_played_count")) {
p_config->last_played_count = strtol(value, &crap, 10);
else if (MATCH("mympd", "loglevel"))
}
else if (MATCH("mympd", "loglevel")) {
p_config->loglevel = strtol(value, &crap, 10);
else if (MATCH("theme", "backgroundcolor"))
}
else if (MATCH("theme", "backgroundcolor")) {
free(p_config->backgroundcolor);
p_config->backgroundcolor = strdup(value);
}
else {
printf("Unkown config option: %s - %s\n", section, name);
return 0; /* unknown section/name, error */
return 0;
}
return 1;
@ -154,26 +175,26 @@ int main(int argc, char **argv) {
//mympd config defaults
t_config config;
config.mpdhost = "127.0.0.1";
config.mpdhost = strdup("127.0.0.1");
config.mpdport = 6600;
config.mpdpass = NULL;
config.webport = "80";
config.webport = strdup("80");
config.ssl = true;
config.sslport = "443";
config.sslcert = "/etc/mympd/ssl/server.pem";
config.sslkey = "/etc/mympd/ssl/server.key";
config.user = "mympd";
config.sslport = strdup("443");
config.sslcert = strdup("/etc/mympd/ssl/server.pem");
config.sslkey = strdup("/etc/mympd/ssl/server.key");
config.user = strdup("mympd");
config.streamport = 8000;
config.streamurl = "";
config.streamurl = strdup("");
config.coverimage = true;
config.coverimagename = "folder.jpg";
config.coverimagename = strdup("folder.jpg");
config.coverimagesize = 240;
config.varlibdir = "/var/lib/mympd";
config.varlibdir = strdup("/var/lib/mympd");
config.stickers = true;
config.mixramp = true;
config.taglist = "Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer";
config.searchtaglist = "Artist,Album,AlbumArtist,Title,Genre,Composer,Performer";
config.browsetaglist = "Artist,Album,AlbumArtist,Genre,Composer,Performer";
config.taglist = strdup("Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer");
config.searchtaglist = strdup("Artist,Album,AlbumArtist,Title,Genre,Composer,Performer");
config.browsetaglist = strdup("Artist,Album,AlbumArtist,Genre,Composer,Performer");
config.smartpls = true;
config.max_elements_per_page = 100;
config.last_played_count = 20;
@ -183,7 +204,7 @@ int main(int argc, char **argv) {
config.syscmds = false;
config.localplayer = true;
config.loglevel = 1;
config.backgroundcolor = "#888";
config.backgroundcolor = strdup("#888");
if (argc == 2) {
printf("Starting myMPD %s\n", MYMPD_VERSION);
@ -292,9 +313,25 @@ int main(int argc, char **argv) {
//cleanup
pthread_join(mpd_client_thread, NULL);
pthread_join(web_server_thread, NULL);
pthread_join(mympd_api_thread, NULL);
tiny_queue_free(web_server_queue);
tiny_queue_free(mpd_client_queue);
tiny_queue_free(mympd_api_queue);
free(config.mpdhost);
free(config.mpdpass);
free(config.webport);
free(config.sslport);
free(config.sslcert);
free(config.sslkey);
free(config.user);
free(config.coverimagename);
free(config.taglist);
free(config.searchtaglist);
free(config.browsetaglist);
free(config.varlibdir);
free(config.etcdir);
free(config.streamurl);
free(config.backgroundcolor);
return EXIT_SUCCESS;
}

View File

@ -1122,6 +1122,7 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) {
len = snprintf(buffer, MAX_SIZE, "{\"type\": \"disconnected\"}");
mpd_client_notify(buffer, len);
mpd_state->conn_state = MPD_FAILURE;
mpd_connection_free(mpd_state->conn);
sleep(3);
return;
}
@ -1160,8 +1161,9 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) {
// fall through
case MPD_DISCONNECT:
case MPD_RECONNECT:
if (mpd_state->conn != NULL)
if (mpd_state->conn != NULL) {
mpd_connection_free(mpd_state->conn);
}
mpd_state->conn = NULL;
mpd_state->conn_state = MPD_DISCONNECTED;
break;
@ -2557,7 +2559,9 @@ static int mpd_client_put_stats(t_mpd_state *mpd_state, char *buffer) {
static void mpd_client_disconnect(t_config *config, t_mpd_state *mpd_state) {
mpd_state->conn_state = MPD_DISCONNECT;
// mpd_client_idle(config, mpd_state);
if (mpd_state->conn != NULL) {
mpd_connection_free(mpd_state->conn);
}
}
static int mpd_client_smartpls_put(t_config *config, char *buffer, const char *playlist) {

View File

@ -69,6 +69,10 @@ static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_reque
static bool mympd_api_read_syscmds(t_config *config, t_mympd_state *mympd_state);
static int mympd_api_syscmd(t_config *config, t_mympd_state *mympd_state, char *buffer, const char *cmd);
static void mympd_api_read_statefiles(t_config *config, t_mympd_state *mympd_state);
static char *state_file_rw_string(t_config *config, const char *name, const char *def_value);
static bool state_file_rw_bool(t_config *config, const char *name, const bool def_value);
static long state_file_rw_long(t_config *config, const char *name, const long def_value);
static bool state_file_write(t_config *config, const char *name, const char *value);
static int mympd_api_put_settings(t_config *config, t_mympd_state *mympd_state, char *buffer);
@ -327,112 +331,129 @@ static int mympd_api_syscmd(t_config *config, t_mympd_state *mympd_state, char *
}
static void mympd_api_read_statefiles(t_config *config, t_mympd_state *mympd_state) {
char *crap;
char value[400];
LOG_INFO() printf("Reading states\n");
if (state_file_read(config, "notificationWeb", value)) {
if (strcmp(value, "true") == 0)
mympd_state->notificationWeb = true;
else
mympd_state->notificationWeb = false;
}
else {
mympd_state->notificationWeb = false;
state_file_write(config, "notificationWeb", "false");
}
mympd_state->notificationWeb = state_file_rw_bool(config, "notificationWeb", false);
mympd_state->notificationPage = state_file_rw_bool(config, "notificationPage", true);
mympd_state->autoPlay = state_file_rw_bool(config, "autoPlay", false);
mympd_state->jukeboxMode = state_file_rw_long(config, "jukeboxMode", JUKEBOX_OFF);
mympd_state->jukeboxPlaylist = state_file_rw_string(config, "jukeboxPlaylist", "Database");
mympd_state->jukeboxQueueLength = state_file_rw_long(config, "jukeboxQueueLength", 1);
mympd_state->colsQueueCurrent = state_file_rw_string(config, "colsQueueCurrent", "[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state->colsSearch = state_file_rw_string(config, "colsSearch", "[\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state->colsBrowseDatabase = state_file_rw_string(config, "colsBrowseDatabase", "[\"Track\",\"Title\",\"Duration\"]");
mympd_state->colsBrowsePlaylistsDetail = state_file_rw_string(config, "colsBrowsePlaylistsDetail", "[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state->colsBrowseFilesystem = state_file_rw_string(config, "colsBrowseFilesystem", "[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state->colsPlayback = state_file_rw_string(config, "colsPlayback", "[\"Artist\",\"Album\"]");
mympd_state->colsQueueLastPlayed = state_file_rw_string(config, "colsQueueLastPlayed", "[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]");
}
if (state_file_read(config, "notificationPage", value)) {
if (strcmp(value, "true") == 0)
mympd_state->notificationPage = true;
else
mympd_state->notificationPage = false;
static char *state_file_rw_string(t_config *config, const char *name, const char *def_value) {
char cfg_file[400];
char *line = NULL;
size_t n = 0;
ssize_t read;
if (!validate_string(name)) {
return NULL;
}
snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name);
FILE *fp = fopen(cfg_file, "r");
if (fp == NULL) {
printf("Error opening %s\n", cfg_file);
state_file_write(config, name, def_value);
return NULL;
}
read = getline(&line, &n, fp);
if (read > 0) {
LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, line);
}
fclose(fp);
if (read > 0) {
return line;
}
else {
mympd_state->notificationPage = true;
state_file_write(config, "notificationPage", "true");
free(line);
return NULL;
}
}
if (state_file_read(config, "autoPlay", value)) {
if (strcmp(value, "true") == 0)
mympd_state->autoPlay = true;
else
mympd_state->autoPlay = false;
}
else {
mympd_state->autoPlay = false;
state_file_write(config, "autoPlay", "false");
}
static bool state_file_rw_bool(t_config *config, const char *name, const bool def_value) {
char cfg_file[400];
char *line = NULL;
size_t n = 0;
ssize_t read;
bool value = def_value;
if (state_file_read(config, "jukeboxMode", value))
mympd_state->jukeboxMode = strtol(value, &crap, 10);
else {
mympd_state->jukeboxMode = JUKEBOX_OFF;
state_file_write(config, "jukeboxMode", "0");
if (!validate_string(name))
return def_value;
snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name);
FILE *fp = fopen(cfg_file, "r");
if (fp == NULL) {
printf("Error opening %s\n", cfg_file);
state_file_write(config, name, def_value == true ? "true" : "false");
return def_value;
}
read = getline(&line, &n, fp);
if (read > 0) {
LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, line);
value = strcmp(line, "true") == 0 ? true : false;
}
fclose(fp);
free(line);
return value;
}
if (state_file_read(config, "jukeboxPlaylist", value))
mympd_state->jukeboxPlaylist = strdup(value);
else {
mympd_state->jukeboxPlaylist = strdup("Database");
state_file_write(config, "jukeboxPlaylist", "Database");
static long state_file_rw_long(t_config *config, const char *name, const long def_value) {
char cfg_file[400];
char *line = NULL;
char *crap;
size_t n = 0;
ssize_t read;
long value = def_value;
if (!validate_string(name))
return def_value;
snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name);
FILE *fp = fopen(cfg_file, "r");
if (fp == NULL) {
printf("Error opening %s\n", cfg_file);
char p_value[65];
snprintf(p_value, 65, "%ld", def_value);
state_file_write(config, name, p_value);
return def_value;
}
read = getline(&line, &n, fp);
if (read > 0) {
LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, line);
value = strtol(line, &crap, 10);
}
fclose(fp);
free(line);
return value;
}
if (state_file_read(config, "jukeboxQueueLength", value))
mympd_state->jukeboxQueueLength = strtol(value, &crap, 10);
else {
mympd_state->jukeboxQueueLength = 1;
state_file_write(config, "jukeboxQueueLength", "1");
}
if (state_file_read(config, "colsQueueCurrent", value))
mympd_state->colsQueueCurrent = strdup(value);
else {
mympd_state->colsQueueCurrent = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
state_file_write(config, "colsQueueCurrent", mympd_state->colsQueueCurrent);
}
if (state_file_read(config, "colsSearch", value))
mympd_state->colsSearch = strdup(value);
else {
mympd_state->colsSearch = strdup("[\"Title\",\"Artist\",\"Album\",\"Duration\"]");
state_file_write(config, "colsSearch", mympd_state->colsSearch);
}
if (state_file_read(config, "colsBrowseDatabase", value))
mympd_state->colsBrowseDatabase = strdup(value);
else {
mympd_state->colsBrowseDatabase = strdup("[\"Track\",\"Title\",\"Duration\"]");
state_file_write(config, "colsBrowseDatabase", mympd_state->colsBrowseDatabase);
}
if (state_file_read(config, "colsBrowsePlaylistsDetail", value))
mympd_state->colsBrowsePlaylistsDetail = strdup(value);
else {
mympd_state->colsBrowsePlaylistsDetail = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
state_file_write(config, "colsBrowsePlaylistsDetail", mympd_state->colsBrowsePlaylistsDetail);
}
if (state_file_read(config, "colsBrowseFilesystem", value))
mympd_state->colsBrowseFilesystem = strdup(value);
else {
mympd_state->colsBrowseFilesystem = strdup("[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
state_file_write(config, "colsBrowseFilesystem", mympd_state->colsBrowseFilesystem);
}
if (state_file_read(config, "colsPlayback", value))
mympd_state->colsPlayback = strdup(value);
else {
mympd_state->colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]");
state_file_write(config, "colsPlayback", mympd_state->colsPlayback);
}
if (state_file_read(config, "colsQueueLastPlayed", value))
mympd_state->colsQueueLastPlayed = strdup(value);
else {
mympd_state->colsQueueLastPlayed = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]");
state_file_write(config, "colsQueueLastPlayed", mympd_state->colsQueueLastPlayed);
static bool state_file_write(t_config *config, const char *name, const char *value) {
char tmp_file[400];
char cfg_file[400];
if (!validate_string(name))
return false;
snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name);
snprintf(tmp_file, 400, "%s/tmp/%s", config->varlibdir, name);
FILE *fp = fopen(tmp_file, "w");
if (fp == NULL) {
printf("Error opening %s\n", tmp_file);
return false;
}
fprintf(fp, "%s", value);
fclose(fp);
if (rename(tmp_file, cfg_file) == -1) {
printf("Error renaming file from %s to %s\n", tmp_file, cfg_file);
return false;
}
return true;
}
static int mympd_api_put_settings(t_config *config, t_mympd_state *mympd_state, char *buffer) {

View File

@ -152,19 +152,28 @@ void *tiny_queue_shift(tiny_queue_t *queue, int timeout) {
}
}
//queue has entry
struct tiny_msg_t* current_head = queue->head;
void *data = current_head->data;
if (queue->head == queue->tail) {
queue->head = queue->tail = NULL;
if (queue->head != NULL) {
struct tiny_msg_t* current_head = queue->head;
void *data = current_head->data;
if (queue->head == queue->tail) {
queue->head = queue->tail = NULL;
}
else {
queue->head = queue->head->next;
}
free(current_head);
queue->length--;
rc = pthread_mutex_unlock(&queue->mutex);
if (rc != 0) {
printf("Error in pthread_mutex_unlock: %d\n", rc);
}
return data;
}
else {
queue->head = queue->head->next;
rc = pthread_mutex_unlock(&queue->mutex);
if (rc != 0) {
printf("Error in pthread_mutex_unlock: %d\n", rc);
}
return NULL;
}
free(current_head);
queue->length--;
rc = pthread_mutex_unlock(&queue->mutex);
if (rc != 0) {
printf("Error in pthread_mutex_unlock: %d\n", rc);
}
return data;
}