tools/daemon/smud_connections.cpp
author David Anderson <dvander@alliedmods.net>
Sat Nov 29 20:07:43 2008 -0600 (2008-11-29)
changeset 2444 5565c93eee91
parent 2216 536bf8a393e5
child 2462 6040047a9da1
permissions -rw-r--r--
Various nits on smupdate tool (bug 3351, r=pred).
dvander@2444
     1
#include <assert.h>
pred@2129
     2
#include "smud_connections.h"
pred@2129
     3
#include "smud.h"
pred@2129
     4
pred@2129
     5
ConnectionPool::ConnectionPool()
pred@2129
     6
{
pred@2129
     7
	pthread_mutex_init(&m_AddLock, NULL);
pred@2129
     8
	m_timeOut = 1000;
pred@2129
     9
}
pred@2129
    10
pred@2129
    11
ConnectionPool::~ConnectionPool()
pred@2129
    12
{
pred@2129
    13
	pthread_mutex_destroy(&m_AddLock);
pred@2129
    14
}
pred@2129
    15
pred@2129
    16
void ConnectionPool::AddConnection( int fd )
pred@2129
    17
{
pred@2129
    18
	smud_connection *connection = new smud_connection(fd);
pred@2129
    19
pred@2129
    20
	pthread_mutex_lock(&m_AddLock);
pred@2129
    21
	m_AddQueue.push_back(connection);
pred@2129
    22
	pthread_mutex_unlock(&m_AddLock);
pred@2129
    23
}
pred@2129
    24
pred@2129
    25
void ConnectionPool::Process( bool *terminate )
pred@2129
    26
{
pred@2129
    27
	struct timespec ts_wait;
pred@2129
    28
pred@2129
    29
	ts_wait.tv_sec = 0;
pred@2129
    30
	ts_wait.tv_nsec = 50000000; /* 50ms */
pred@2129
    31
pred@2129
    32
	std::list<smud_connection *>::iterator iter;
pred@2129
    33
	smud_connection *con = NULL;
pred@2129
    34
pred@2129
    35
	while (1)
pred@2129
    36
	{
pred@2129
    37
		if (*terminate)
pred@2129
    38
		{
pred@2129
    39
			return;
pred@2129
    40
		}
pred@2129
    41
pred@2129
    42
		iter = m_Links.begin();
pred@2129
    43
pred@2129
    44
		QueryResult result = QueryResult_Continue;
pred@2129
    45
		int pollReturn = 0;
pred@2129
    46
pred@2129
    47
		/* Add all connections that want processing to the sets */
pred@2129
    48
		while (iter != m_Links.end())
pred@2129
    49
		{
dvander@2444
    50
			con = *iter;
pred@2129
    51
pred@2129
    52
			pollReturn = poll(&(con->pollData), 1, 0);
dvander@2444
    53
			assert(pollReturn <= 1);
pred@2129
    54
pred@2129
    55
			if (pollReturn == -1)
pred@2129
    56
			{
pred@2129
    57
				//Something went badly wrong or the connection closed.
pred@2129
    58
				result = QueryResult_Complete;
pred@2129
    59
			}
pred@2129
    60
			else if (pollReturn == 1)
pred@2129
    61
			{
pred@2129
    62
				//Poll returns the number of sockets available (which can only ever be 1)
pred@2129
    63
				result = ProcessConnection(con);
pred@2129
    64
			}
pred@2129
    65
pred@2129
    66
			if (result == QueryResult_Complete)
pred@2129
    67
			{
pred@2129
    68
				iter = m_Links.erase(iter);
dvander@2444
    69
#if defined DEBUG
dvander@2444
    70
				fprintf(stdout, "Closing socket %d\n", con->fd);
dvander@2444
    71
#endif
pred@2129
    72
				closesocket(con->fd);
pred@2129
    73
				delete con;
pred@2129
    74
				continue;
pred@2129
    75
			}
pred@2129
    76
pred@2129
    77
			iter++;
pred@2129
    78
		}
pred@2129
    79
pred@2129
    80
		/* Add new items to process */
pred@2129
    81
pred@2129
    82
		iter = m_Links.end();
pred@2129
    83
		pthread_mutex_lock(&m_AddLock);
pred@2129
    84
		m_Links.splice(iter, m_AddQueue);
pred@2129
    85
		pthread_mutex_unlock(&m_AddLock);
pred@2129
    86
pred@2129
    87
		nanosleep(&ts_wait, NULL);
pred@2129
    88
	}
pred@2129
    89
}
pred@2129
    90
pred@2129
    91
QueryResult ConnectionPool::ProcessConnection( smud_connection *con )
pred@2129
    92
{
pred@2129
    93
	switch (con->state)
pred@2129
    94
	{
pred@2129
    95
		case ConnectionState_ReadQueryHeader:
pred@2129
    96
		{
pred@2129
    97
			ReadQueryHeader(con);
pred@2129
    98
			break;
pred@2129
    99
		}
pred@2129
   100
pred@2129
   101
		case ConnectionState_ReadQueryData:
pred@2129
   102
		{
pred@2129
   103
			ReadQueryContent(con);
pred@2129
   104
			break;
pred@2129
   105
		}
pred@2129
   106
pred@2129
   107
		case ConnectionState_ReplyQuery:
pred@2129
   108
		{
pred@2129
   109
			ReplyQuery(con);
pred@2129
   110
			break;
pred@2129
   111
		}
pred@2129
   112
pred@2129
   113
		case ConnectionState_SendingFiles:
pred@2129
   114
		{
pred@2129
   115
			SendFile(con);
pred@2129
   116
			break;
pred@2129
   117
		}
pred@2129
   118
pred@2129
   119
		case ConnectionState_SendUnknownList:
pred@2129
   120
		{
pred@2129
   121
			SendUnknownList(con);
pred@2129
   122
			break;
pred@2129
   123
		}
pred@2129
   124
pred@2129
   125
		case ConnectionState_Complete:
pred@2129
   126
		{
pred@2129
   127
			break;
pred@2129
   128
		}
pred@2129
   129
	}
pred@2129
   130
pred@2129
   131
	if (con->state == ConnectionState_Complete)
pred@2129
   132
	{
pred@2129
   133
		printf("Ending connection because it marked itself as finished\n");
pred@2129
   134
		return QueryResult_Complete;
pred@2129
   135
	}
pred@2129
   136
pred@2129
   137
	if (con->start + m_timeOut < time(NULL))
pred@2129
   138
	{
pred@2129
   139
		printf("Ending connection because it has passed maximum allowed time\n");
pred@2129
   140
		return QueryResult_Complete;
pred@2129
   141
	}
pred@2129
   142
pred@2129
   143
	return QueryResult_Continue;
pred@2129
   144
}
pred@2129
   145
pred@2129
   146
void ConnectionPool::ReadQueryHeader( smud_connection *con )
pred@2129
   147
{
pred@2129
   148
	char data[11];
pred@2129
   149
pred@2129
   150
	if (recv(con->fd, data, sizeof(data), 0) == -1)
pred@2129
   151
	{
pred@2129
   152
		if (errno != EAGAIN && errno != EWOULDBLOCK)
pred@2129
   153
		{
pred@2129
   154
			con->state = ConnectionState_Complete;
pred@2129
   155
		}
pred@2129
   156
pred@2129
   157
		return;
pred@2129
   158
	}
pred@2129
   159
pred@2129
   160
	if (data[0] != 'A' || data[1] != 'G')
pred@2129
   161
	{
pred@2129
   162
		con->state = ConnectionState_Complete;
pred@2129
   163
		return;
pred@2129
   164
	}
pred@2129
   165
pred@2129
   166
	//Ignore the next 8 bytes for the moment. Versioning data is currently unused
pred@2129
   167
	// uint16[4] - source version major/minor/something/rev
pred@2129
   168
pred@2129
   169
	con->sentSums = data[10];
pred@2129
   170
pred@2129
   171
	con->state = ConnectionState_ReadQueryData;
dvander@2444
   172
#if defined DEBUG
dvander@2444
   173
	fprintf(stdout, "Query Header Read Complete, %i md5's expected\n", con->sentSums);
dvander@2444
   174
#endif
pred@2129
   175
}
pred@2129
   176
pred@2129
   177
void ConnectionPool::ReplyQuery(smud_connection *con)
pred@2129
   178
{
pred@2129
   179
	char data[12];
pred@2129
   180
	data[0] = 'A';
pred@2129
   181
	data[1] = 'G';
pred@2129
   182
pred@2129
   183
	data[2] = (char)Update_Unknown; //unused versioning crap
pred@2129
   184
	*(short *)&data[3] = 1;
pred@2129
   185
	*(short *)&data[5] = 0;
pred@2129
   186
	*(short *)&data[7] = 0;
pred@2129
   187
	*(short *)&data[9] = 3;
pred@2129
   188
pred@2129
   189
	data[11] = (char)con->sendCount;
pred@2129
   190
pred@2129
   191
	if (send(con->fd, data, sizeof(data), 0) == -1)
pred@2129
   192
	{
pred@2129
   193
		if (errno != EAGAIN && errno != EWOULDBLOCK)
pred@2129
   194
		{
pred@2129
   195
			con->state = ConnectionState_Complete;
pred@2129
   196
		}
pred@2129
   197
pred@2129
   198
		return;
pred@2129
   199
	}
pred@2129
   200
	
pred@2129
   201
	//Now we need more send sub functions for all the damn files. Geh.
pred@2129
   202
	//Alternatively we could just send all at once here. Could make for a damn big query. 100k anyone?
pred@2129
   203
pred@2129
   204
	con->state = ConnectionState_SendingFiles;
dvander@2444
   205
#if defined DEBUG
pred@2129
   206
	printf("Query Reply Header Complete\n");
dvander@2444
   207
#endif
pred@2129
   208
}
pred@2129
   209
pred@2129
   210
void ConnectionPool::ReadQueryContent( smud_connection *con )
pred@2129
   211
{
pred@2129
   212
	char *data = new char[16*(con->sentSums)]();
pred@2129
   213
pred@2129
   214
	if (recv(con->fd, data, 16*(con->sentSums), 0) == -1)
pred@2129
   215
	{
pred@2129
   216
		if (errno != EAGAIN && errno != EWOULDBLOCK)
pred@2129
   217
		{
pred@2129
   218
			con->state = ConnectionState_Complete;
pred@2129
   219
		}
pred@2129
   220
pred@2129
   221
		delete [] data;
pred@2129
   222
		return;
pred@2129
   223
	}
pred@2129
   224
pred@2129
   225
	con->shouldSend = new MD5Status[con->sentSums]();
pred@2129
   226
	con->fileLocation = new int[con->sentSums]();
pred@2129
   227
	con->headerSent = new bool[con->sentSums]();
pred@2129
   228
pred@2129
   229
	for (int i=0; i<con->sentSums; i++)
pred@2129
   230
	{
pred@2129
   231
		con->fileLocation[i] = -1;
pred@2129
   232
		con->shouldSend[i] = GetMD5UpdateStatus(data + (16*i), con, i);
pred@2129
   233
		
pred@2129
   234
		if (con->shouldSend[i] == MD5Status_NeedsUpdate)
pred@2129
   235
		{
dvander@2444
   236
#if defined DEBUG
dvander@2444
   237
			fprintf(stdout, "File %i needs updating\n", i);
dvander@2444
   238
#endif
pred@2129
   239
			con->sendCount++;
pred@2129
   240
			con->headerSent[i] = false;
pred@2129
   241
			continue;
pred@2129
   242
		}
pred@2129
   243
pred@2129
   244
		if (con->shouldSend[i] == MD5Status_Unknown)
pred@2129
   245
		{
dvander@2444
   246
#if defined DEBUG
dvander@2444
   247
			fprintf(stdout, "File %i is unknown\n", i);
dvander@2444
   248
#endif
pred@2129
   249
			con->unknownCount++;
pred@2129
   250
		}
pred@2129
   251
	}
pred@2129
   252
pred@2129
   253
	con->state = ConnectionState_ReplyQuery;
pred@2129
   254
	con->pollData.events = POLLOUT;
pred@2129
   255
	delete [] data;
dvander@2444
   256
#if defined DEBUG
dvander@2444
   257
	fprintf(stdout, "Query Data Read Complete\n");
dvander@2444
   258
#endif
pred@2129
   259
}
pred@2129
   260
pred@2129
   261
MD5Status ConnectionPool::GetMD5UpdateStatus( const char *md5 , smud_connection *con, int fileNum)
pred@2129
   262
{
pred@2129
   263
	//Try find a file with this name in some directory.
pred@2129
   264
	char path[100] = "./md5/";
pred@2129
   265
	char temp[4];
pred@2129
   266
	char md5String[33] = "";
pred@2129
   267
pred@2129
   268
	for (int i=0; i<16; i++)
pred@2129
   269
	{
pred@2129
   270
		snprintf(temp, sizeof(temp), "%02x", (unsigned char)md5[i]);
pred@2129
   271
		strcat(md5String, temp);
pred@2129
   272
	}
pred@2129
   273
pred@2129
   274
	strcat(path, md5String);
pred@2129
   275
dvander@2444
   276
#if defined DEBUG
dvander@2444
   277
	fprintf(stdout, "checking for file \"%s\"\n", path);
dvander@2444
   278
#endif
pred@2129
   279
	
pred@2129
   280
	FILE *file = fopen(path, "r");
pred@2129
   281
pred@2129
   282
	if (file == NULL)
pred@2129
   283
	{
pred@2129
   284
		printf("Couldn't find file!\n");
pred@2129
   285
		return MD5Status_Unknown;
pred@2129
   286
	}
pred@2129
   287
pred@2129
   288
	char latestMD5[33];
pred@2129
   289
	fgets(latestMD5, 33, file);
dvander@2444
   290
#if defined DEBUG
dvander@2444
   291
	fprintf(stdout, "Latest md5 is: %s\n", latestMD5);
dvander@2444
   292
#endif
pred@2129
   293
pred@2129
   294
	if (strcmp(latestMD5, md5String) == 0)
pred@2129
   295
	{
pred@2216
   296
		fclose(file);
pred@2129
   297
		return MD5Status_Current;
pred@2129
   298
	}
pred@2129
   299
pred@2129
   300
	char filename[100];
pred@2140
   301
	filename[0] = '\n';
pred@2140
   302
pred@2140
   303
	while (filename[0] == '\n')
pred@2129
   304
	{
pred@2140
   305
		fgets(filename, sizeof(filename), file);
pred@2129
   306
	}
pred@2129
   307
pred@2141
   308
	if (filename[strlen(filename)-1] == '\n')
pred@2141
   309
	{	
pred@2141
   310
		filename[strlen(filename)-1] = '\0';
pred@2141
   311
	}
pred@2141
   312
pred@2213
   313
	fclose(file);
pred@2140
   314
dvander@2444
   315
#if defined DEBUG
dvander@2444
   316
	fprintf(stdout, "Filename is %s\n", filename);
dvander@2444
   317
#endif
pred@2129
   318
pred@2129
   319
	//We now need to match this filename with one of our mmap'd files in memory and store it until send gets called.
pred@2129
   320
	for (int i=0; i<NUM_FILES; i++)
pred@2129
   321
	{
pred@2129
   322
		if (strcmp(fileNames[i], filename) == 0)
pred@2129
   323
		{
pred@2129
   324
			con->fileLocation[fileNum] = i;
dvander@2444
   325
#if defined DEBUG
dvander@2444
   326
			fprintf(stdout, "File %i mapped to local file %i\n", fileNum, i);
dvander@2444
   327
#endif
pred@2129
   328
			return MD5Status_NeedsUpdate;
pred@2129
   329
		}
pred@2129
   330
	}
pred@2129
   331
pred@2129
   332
	return MD5Status_Unknown;
pred@2129
   333
}
pred@2129
   334
pred@2129
   335
void ConnectionPool::SendFile( smud_connection *con )
pred@2129
   336
{
pred@2129
   337
	//Find the next file to send.
pred@2129
   338
	while (con->currentFile < con->sentSums &&
pred@2129
   339
			con->shouldSend[con->currentFile] != MD5Status_NeedsUpdate)
pred@2129
   340
	{
pred@2129
   341
		con->currentFile++;
pred@2129
   342
	}
pred@2129
   343
pred@2129
   344
	//All files have been sent.
pred@2129
   345
	if (con->currentFile >= con->sentSums)
pred@2129
   346
	{
dvander@2444
   347
#if defined DEBUG
dvander@2444
   348
		fprintf(stdout, "All files sent!\n");
dvander@2444
   349
#endif
pred@2129
   350
		con->state = ConnectionState_SendUnknownList;
pred@2129
   351
		return;
pred@2129
   352
	}
pred@2129
   353
pred@2129
   354
	void *file = fileLocations[con->fileLocation[con->currentFile]];
pred@2129
   355
	int filelength = fileLength[con->fileLocation[con->currentFile]];
pred@2129
   356
dvander@2444
   357
#if defined DEBUG
dvander@2444
   358
	fprintf(stdout, "Sending file of length %i\n", filelength);
dvander@2444
   359
	fprintf(stdout,
dvander@2444
   360
			"Current file index is: %i, maps to file index: %i\n",
dvander@2444
   361
			con->currentFile,
dvander@2444
   362
			con->fileLocation[con->currentFile]);
dvander@2444
   363
#endif
pred@2129
   364
pred@2129
   365
	if (!con->headerSent[con->currentFile])
pred@2129
   366
	{
pred@2129
   367
		char buffer[5];
pred@2129
   368
		buffer[0] = con->currentFile;
pred@2129
   369
		*((int *)&buffer[1]) = filelength;
pred@2129
   370
pred@2129
   371
		if (send(con->fd, buffer, 5, 0) == -1)
pred@2129
   372
		{
pred@2129
   373
			if (errno != EAGAIN && errno != EWOULDBLOCK)
pred@2129
   374
			{
pred@2129
   375
				con->state = ConnectionState_Complete;
pred@2129
   376
			}
pred@2129
   377
pred@2129
   378
			return;
pred@2129
   379
		}
pred@2129
   380
pred@2129
   381
		con->headerSent[con->currentFile] = true;
pred@2129
   382
	}
pred@2129
   383
pred@2129
   384
	if (send(con->fd, file, filelength, 0) == -1)
pred@2129
   385
	{
pred@2129
   386
		if (errno != EAGAIN && errno != EWOULDBLOCK)
pred@2129
   387
		{
pred@2129
   388
			con->state = ConnectionState_Complete;
pred@2129
   389
		}
pred@2129
   390
pred@2129
   391
		return;
pred@2129
   392
	}
pred@2129
   393
pred@2129
   394
	con->currentFile++;
dvander@2444
   395
#if defined DEBUG
dvander@2444
   396
	fprintf(stdout, "Sent a file!: %s\n", fileNames[con->fileLocation[con->currentFile-1]]);
dvander@2444
   397
#endif
pred@2129
   398
}
pred@2129
   399
pred@2129
   400
void ConnectionPool::SendUnknownList( smud_connection *con )
pred@2129
   401
{
pred@2129
   402
	int size = con->unknownCount+1;
pred@2129
   403
	char *packet = new char[size]();
pred@2129
   404
pred@2129
   405
	packet[0] = con->unknownCount;
pred@2129
   406
dvander@2444
   407
#if defined DEBUG
dvander@2444
   408
	fprintf(stdout, "%i Files are unknown\n", con->unknownCount);
dvander@2444
   409
#endif
pred@2129
   410
pred@2129
   411
	int i=1;
pred@2129
   412
pred@2129
   413
	for (int j=0; j<con->sentSums; j++)
pred@2129
   414
	{
pred@2129
   415
		if (con->shouldSend[j] == MD5Status_Unknown)
pred@2129
   416
		{
pred@2129
   417
			packet[i] = j;
pred@2129
   418
			i++;
pred@2129
   419
		}
pred@2129
   420
	}
pred@2129
   421
pred@2129
   422
	if (send(con->fd, packet, size, 0) == -1)
pred@2129
   423
	{
pred@2129
   424
		if (errno != EAGAIN && errno != EWOULDBLOCK)
pred@2129
   425
		{
pred@2129
   426
			con->state = ConnectionState_Complete;
pred@2129
   427
		}
pred@2129
   428
pred@2129
   429
		return;
pred@2129
   430
	}
pred@2129
   431
pred@2129
   432
	con->state = ConnectionState_Complete;
dvander@2444
   433
#if defined DEBUG
dvander@2444
   434
	fprintf(stdout, "Unknowns Sent\n");
dvander@2444
   435
#endif
pred@2129
   436
}
dvander@2444
   437