示例#1
0
/******************************************************
   Tries to execute the job and then wait for it
   to finish running.
   
   POST: Returns true if the job was successfully started
         and finished running. Returns false if there
         was an error.
*/
void PipeManager::execute() {

   // create arrays to pass to pipe system call
   createPipes();
   
   // create last child first
   if (!createLastChild())
      return;
      
   // create all middle children in reverse order
   int num_middle_children = my_command.getCommands().size() - 2; // - 2 because we're doing first and last separately
   for (int childCtr = num_middle_children; childCtr > 0; childCtr--) {
      
      if (!createMiddleChild(childCtr))
         return;
   }
   
   if (!createFirstChild())
      return;
   
   // must be in parent now, close pipes and wait for children
   closePipes();
   waitForChildren();
   deletePipes();

   return;   
}
示例#2
0
void eConsoleAppContainer::kill()
{
	if ( killstate != -1 && pid != -1 )
	{
		eDebug("user kill(SIGKILL) console App");
		killstate=-1;
		/*
		 * Use a negative pid value, to signal the whole process group
		 * ('pid' might not even be running anymore at this point)
		 */
		::kill(-pid, SIGKILL);
		closePipes();
	}
	while( outbuf.size() ) // cleanup out buffer
	{
		queue_data d = outbuf.front();
		outbuf.pop();
		delete [] d.data;
	}
	in = 0;
	out = 0;
	err = 0;

	for (int i=0; i < 3; ++i)
	{
		if ( filefd[i] > 0 )
			close(filefd[i]);
	}
}
示例#3
0
文件: ipc.c 项目: hno/fribid
void regutil_storeCertificates(Plugin *plugin, const char *certs) {
    PipeInfo pipeinfo;
    
    openPipes(&pipeinfo, plugin);
    sendHeader(&pipeinfo, plugin, PC_StoreCertificates);
    
    pipe_sendOptionalString(pipeinfo.out, certs);
    
    plugin->lastError = waitReply(&pipeinfo);
    closePipes(&pipeinfo);    
}
示例#4
0
文件: ipc.c 项目: hno/fribid
char *version_getVersion(Plugin *plugin) {
    PipeInfo pipeinfo;
    
    openPipes(&pipeinfo, plugin);
    sendHeader(&pipeinfo, plugin, PC_GetVersion);
    pipe_finishCommand(pipeinfo.out);
    
    char *version = pipe_readString(pipeinfo.in);
    closePipes(&pipeinfo);
    return version;
}
示例#5
0
文件: ipc.c 项目: hno/fribid
int sign_performAction_Authenticate(Plugin *plugin) {
    PipeInfo pipeinfo;
    
    openPipes(&pipeinfo, plugin);
    sendHeader(&pipeinfo, plugin, PC_Authenticate);
    sendSignCommon(&pipeinfo, plugin);
    
    plugin->lastError = waitReply(&pipeinfo);
    plugin->info.auth.signature = pipe_readString(pipeinfo.in);
    closePipes(&pipeinfo);
    return plugin->lastError;
}
示例#6
0
void execPipe(int i) {
	if( fork()==0 ) {
		// Case A: First Pipe
		if(i == 0) {
			dup2(comtab[i].iofd[1], STDOUT_FILENO);
			execvp(comtab[i].atptr->args[0],comtab[curr_cmd].atptr->args);
			closePipes();
		}
		// Case B: Last Command
		else if (curr_cmd) {
			dup2(comtab[i-1].iofd[1], STDIN_FILENO);
			execvp(comtab[i].atptr->args[0],comtab[curr_cmd].atptr->args);
			closePipes();
		}
		// Case C: Middle of Pipe
		else {
			dup2(comtab[i-1].iofd[0], STDIN_FILENO);
			dup2(comtab[i].iofd[1], STDOUT_FILENO);
			closePipes();
		}
	} // End-if
}
示例#7
0
文件: interface.cpp 项目: chagge/AI
void Interface::finalizePipe() {
	return;
	while(1) {
		std::string x = (toString(18) + ",18\n");
		fwrite(x.c_str(), sizeof(char), x.length(), infoIn);
		char *str = inputString(infoOut,10);
		if(str[0] == 'D') {
			free(str);
			break;
		}
		free(str);
	}
	closePipes();
}
示例#8
0
文件: ipc.c 项目: streambag/fribid
int sign_performAction_Sign(Plugin *plugin) {
    PipeInfo pipeinfo;
    
    openPipes(&pipeinfo, plugin);
    sendHeader(&pipeinfo, plugin, PC_Sign);
    sendSignCommon(&pipeinfo, plugin);
    
    pipe_sendString(pipeinfo.out, plugin->info.sign.message);
    pipe_sendOptionalString(pipeinfo.out, plugin->info.sign.invisibleMessage);
    
    plugin->lastError = waitReply(&pipeinfo);
    plugin->info.auth.signature = pipe_readString(pipeinfo.in);
    closePipes(&pipeinfo);
    return plugin->lastError;
}
int closeServer() {
	int iResult = 0;
	isForceQuit = true;
	if (!isConnectionEstablished) {
		iResult = closesocket(ListenSocket);
		ListenSocket = INVALID_SOCKET;
		WSACleanup();
		return iResult;
	}
	else {
		closePipes();
		closeClientSocket();
		WSACleanup();
		return 0;
	}
	return -1;
}
示例#10
0
void eConsoleAppContainer::readyRead(int what)
{
	bool hungup = what & eSocketNotifier::Hungup;
	if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
	{
//		eDebug("what = %d");
		char buf[2049];
		int rd;
		while((rd = read(fd[0], buf, 2048)) > 0)
		{
			buf[rd]=0;
			/*emit*/ dataAvail(buf);
			stdoutAvail(buf);
			if ( filefd[1] > 0 )
				::write(filefd[1], buf, rd);
			if (!hungup)
				break;
		}
	}
	readyErrRead(eSocketNotifier::Priority|eSocketNotifier::Read); /* be sure to flush all data which might be already written */
	if (hungup)
	{
		eDebug("child has terminated");
		closePipes();
		int childstatus;
		int retval = killstate;
		/*
		 * We have to call 'wait' on the child process, in order to avoid zombies.
		 * Also, this gives us the chance to provide better exit status info to appClosed.
		 */
		if (::waitpid(pid, &childstatus, 0) > 0)
		{
			if (WIFEXITED(childstatus))
			{
				retval = WEXITSTATUS(childstatus);
			}
		}
		/*emit*/ appClosed(retval);
	}
}
示例#11
0
文件: ipc.c 项目: hno/fribid
char *regutil_createRequest(Plugin *plugin) {
    PipeInfo pipeinfo;
    
    openPipes(&pipeinfo, plugin);
    sendHeader(&pipeinfo, plugin, PC_CreateRequest);
    
    // Send password policy
    pipe_sendInt(pipeinfo.out, plugin->info.regutil.input.minPasswordLength);
    pipe_sendInt(pipeinfo.out, plugin->info.regutil.input.minPasswordNonDigits);
    pipe_sendInt(pipeinfo.out, plugin->info.regutil.input.minPasswordDigits);
    
    // Send PKCS10 info
    RegutilPKCS10 *pkcs10 = plugin->info.regutil.input.pkcs10;
    while (pkcs10) {
        pipe_sendInt(pipeinfo.out, PLS_MoreData);
        
        pipe_sendInt(pipeinfo.out, pkcs10->keyUsage);
        pipe_sendInt(pipeinfo.out, pkcs10->keySize);
        pipe_sendOptionalString(pipeinfo.out, pkcs10->subjectDN);
        pipe_sendInt(pipeinfo.out, pkcs10->includeFullDN);
        
        pkcs10 = pkcs10->next;
    }
    pipe_sendInt(pipeinfo.out, PLS_End);
    
    // Send CMC info
    RegutilCMC *cmc = &plugin->info.regutil.input.cmc;
    pipe_sendOptionalString(pipeinfo.out, cmc->oneTimePassword);
    pipe_sendOptionalString(pipeinfo.out, cmc->rfc2729cmcoid);
    
    plugin->lastError = waitReply(&pipeinfo);
    char *request = pipe_readString(pipeinfo.in);
    if (plugin->lastError) {
        free(request);
        request = NULL;
    }
    
    closePipes(&pipeinfo);
    return request;
}
示例#12
0
文件: main.c 项目: g40st/SysProg_4
// Main program entry point
int main(int argc, char **argv) {
    for (int i = 0; i < (1 + MAX_PLAYERS); i++)
        threadsFree[i] = -1;

    setProgName(argv[0]);
    debugDisable();
    infoPrint("Server Gruppe 01");
    userInit();

    // Create PID lock file or exit if it already exists
    debugPrint("Making sure we're running only once...");
    if (singleton(LOCKFILE) != 0)
        return 1;

    // Socket structure that could be modified by the command line arguments
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(PORT); // Use the default port defined in "common"!

    // Prepare parsing the command line options using getopt()
    debugPrint("Parsing command line options...");
    const char* short_options = "hvp:";
    struct option long_options[] = {
        { "help", no_argument, 0, 'h' },
        { "verbose", no_argument, 0, 'v' },
        { "port", required_argument, 0, 'p' },
        { NULL, 0, NULL, 0 }
    };

    // Actual getopt() loop
    int option_index = 0;
    int loop = 1;
    while (loop != 0) {
        int c = getopt_long(argc, argv, short_options, long_options, &option_index);
        switch (c) {
            // Show help text when using -h
            case 'h':
                show_help();
                exit(1);
                break;

            // Enable verbose (debug) output
            case 'v':
                debugEnable();
                break;

            // Set port to listen on
            case 'p':
                if(optarg) {
                    if (isOnlyDigits(optarg)) {
                        server.sin_port = htons(atoi(optarg));
                    } else {
                        errorPrint("Port has to be a decimal number!");
                        exit(1);
                    }
                }
                break;

            // Unknown option, show error & help message
            default:
            case '?':
                errorPrint("Option not implemented yet -- %s (%c)", argv[optind-1], c);
                show_help();
                exit(1);
                break;

            // All options have been parsed
            case -1:
                loop = 0;
                break;
        }
    }

    infoPrint("Serverport: %i", ntohs(server.sin_port));

    // Create the pipes that will be used to communicate with the loader
    debugPrint("Creating Pipes...");
    if (!createPipes())
        return 1;

    // Actually fork and run the loader as child process
    debugPrint("Forking to run loader...");
    if (!forkLoader())
        return 1;

    debugPrint("Starting Threads...");

    // Start the score agent thread
    if (pthread_create(&threads[0], NULL, scoreThread, NULL) != 0) {
        threadsFree[0] = -2;
        errnoPrint("pthread_create");
        return 1;
    }

    clientInit();

    // Create the listening socket
    debugPrint("Creating socket...");
    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_socket == -1) {
        errnoPrint("socket");
        return 1;
    }

    // Enable address reusing, so we don't get bind errors
    int on = 1;
    setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    // Register our Interrupt Signal handler
    signal(SIGINT, intHandler);

    // Store the listening socket in our user database
    userSetMainSocket(listen_socket);

    // Actually bind the listening socket
    if (bind(listen_socket, (struct sockaddr*) &server, sizeof(struct sockaddr_in)) == -1) {
        errnoPrint("bind");
        close(listen_socket);
        closePipes();
        loaderCloseSharedMemory();
        return 1;
    }

    // Prepare the pselect() timeout data structure
    fd_set fds;
    struct timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = SOCKET_TIMEOUT * 1000000;
    sigset_t blockset;
    sigfillset(&blockset);
    sigdelset(&blockset, SIGINT);

    // Main thread main loop, waiting for new connections
    debugPrint("Waiting for connections...");
    while (getRunning()) {
        // Listen to the listening socket
        if (listen(listen_socket, MAX_QUERYS) == -1) {
            errnoPrint("listen");
            close(listen_socket);
            closePipes();
            loaderCloseSharedMemory();
            cleanCategories();
            return 1;
        }

        // Wait for activity on the listening socket
        FD_ZERO(&fds);
        FD_SET(listen_socket, &fds);
        int retval = pselect(listen_socket + 1, &fds, NULL, NULL, &ts, &blockset);
        if (retval == -1) {
            if (errno == EINTR) {
                // We should exit because the user pressed Ctrl + C
                close(listen_socket);
                closePipes();
                loaderCloseSharedMemory();
                cleanCategories();
                return 0;
            } else {
                // pselect encountered an error!
                errnoPrint("select");
                close(listen_socket);
                closePipes();
                loaderCloseSharedMemory();
                cleanCategories();
                return 1;
            }
        } else if (retval == 0) {
            // Nothing happened and pselect timed out
            continue;
        } else {
            // We have a new connection, accept it
            struct sockaddr_in remote_host;
            socklen_t sin_size = sizeof(struct sockaddr_in);
            int client_socket = accept(listen_socket, (struct sockaddr *) &remote_host, &sin_size);
            if (client_socket == -1) {
                errnoPrint("accept");
                close(listen_socket);
                closePipes();
                loaderCloseSharedMemory();
                cleanCategories();
                return 1;
            }

            debugPrint("Got a new connection! Performing Login...");
            loginHandleSocket(client_socket);
        }
    }

    // Clean up behind ourselves
    close(listen_socket);
    closePipes();
    loaderCloseSharedMemory();
    cleanCategories();

    pthread_exit(NULL);
    return 0;
}
/** Executes a command in a child process. */
static pid_t executeProcess(JNIEnv* env, char** commands, char** environment,
        const char* workingDirectory, jobject inDescriptor,
        jobject outDescriptor, jobject errDescriptor,
        jboolean redirectErrorStream) {

    // Keep track of the system properties fd so we don't close it.
    int androidSystemPropertiesFd = -1;
    char* fdString = getenv("ANDROID_PROPERTY_WORKSPACE");
    if (fdString) {
        androidSystemPropertiesFd = atoi(fdString);
    }

    // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe.
    int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
    for (int i = 0; i < PIPE_COUNT; i++) {
        if (pipe(pipes + i * 2) == -1) {
            jniThrowIOException(env, errno);
            closePipes(pipes, -1);
            return -1;
        }
    }
    int stdinIn = pipes[0];
    int stdinOut = pipes[1];
    int stdoutIn = pipes[2];
    int stdoutOut = pipes[3];
    int stderrIn = pipes[4];
    int stderrOut = pipes[5];
    int statusIn = pipes[6];
    int statusOut = pipes[7];

    pid_t childPid = fork();

    // If fork() failed...
    if (childPid == -1) {
        jniThrowIOException(env, errno);
        closePipes(pipes, -1);
        return -1;
    }

    // If this is the child process...
    if (childPid == 0) {
        /*
         * Note: We cannot malloc() or free() after this point!
         * A no-longer-running thread may be holding on to the heap lock, and
         * an attempt to malloc() or free() would result in deadlock.
         */

        // Replace stdin, out, and err with pipes.
        dup2(stdinIn, 0);
        dup2(stdoutOut, 1);
        if (redirectErrorStream) {
            dup2(stdoutOut, 2);
        } else {
            dup2(stderrOut, 2);
        }

        // Close all but statusOut. This saves some work in the next step.
        closePipes(pipes, statusOut);

        // Make statusOut automatically close if execvp() succeeds.
        fcntl(statusOut, F_SETFD, FD_CLOEXEC);

        // Close remaining unwanted open fds.
        closeNonStandardFds(statusOut, androidSystemPropertiesFd);

        // Switch to working directory.
        if (workingDirectory != NULL) {
            if (chdir(workingDirectory) == -1) {
                goto execFailed;
            }
        }

        // Set up environment.
        if (environment != NULL) {
            extern char** environ; // Standard, but not in any header file.
            environ = environment;
        }

        // Execute process. By convention, the first argument in the arg array
        // should be the command itself. In fact, I get segfaults when this
        // isn't the case.
        execvp(commands[0], commands);

        // If we got here, execvp() failed or the working dir was invalid.
        execFailed:
            int error = errno;
            write(statusOut, &error, sizeof(int));
            close(statusOut);
            exit(error);
    }

    // This is the parent process.

    // Close child's pipe ends.
    close(stdinIn);
    close(stdoutOut);
    close(stderrOut);
    close(statusOut);

    // Check status pipe for an error code. If execvp() succeeds, the other
    // end of the pipe should automatically close, in which case, we'll read
    // nothing.
    int result;
    int count = read(statusIn, &result, sizeof(int));
    close(statusIn);
    if (count > 0) {
        jniThrowIOException(env, result);

        close(stdoutIn);
        close(stdinOut);
        close(stderrIn);

        return -1;
    }

    // Fill in file descriptor wrappers.
    jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn);
    jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut);
    jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn);

    return childPid;
}
示例#14
0
文件: m2.cpp 项目: Andrean/OS_1
int main(){

	
	char str[200];
	char proc1[100];
	char proc2[100];
	char proc3[100];
	char *proc = NULL;
	while(1){

		printf("> ");
		fgets(str,200,stdin);
		
		int str_len = strlen(str);

		if(str_len == 1)
			continue;
		
		str[str_len-1] = 0;
		
		int cur_proc = 1;
		int j = 0;
		for(int i = 0;i<str_len;i++){
			if(cur_proc==1) proc = proc1;
			if(cur_proc==2) proc = proc2;
			if(cur_proc==3) proc = proc3;
			 		
			switch(str[i]){
				
				case ' ':
					break;
				case '|':
					cur_proc++; proc[j] = 0;j = 0;
					break;	
				case 0:
					proc[j] = 0;
					break;
				default:
					proc[j++] = str[i];
					break;		
			}		
		}
		

		printf("Process 1: %s\nProcess 2: %s\nProcess 3: %s\n",proc1,proc2,proc3);

		if(pipe(pipe_out)){
			printf("Pipe error");
			continue;
		}
		if(pipe(pipe_err)){
			printf("Pipe error");
			close(pipe_out[0]);
			close(pipe_out[1]);
			continue;		
		}
	
		int code_fork1 = fork();
		if(code_fork1 < 0 ){
			perror("Error while forking");
			closePipes();
			continue;
		}
		if(code_fork1 == 0){
			if(dup2(pipe_out[1], STDOUT_FILENO) == -1){
				perror("Dup pipe_out error: process1");
				
			}
			if(dup2(pipe_err[1], STDERR_FILENO) == -1){
				perror("Dup pipe_err error: process1");
				
			}
			closePipes();			
			if(execlp(proc1,proc1,NULL)){
				perror("Invalid command process1\n");
						
			}
			
			exit(0);
			
		}
		
		int code_fork2 = fork();
		if(code_fork2 < 0) {
			perror("Error while forking");
			closePipes();
			continue;	
		}
		if(code_fork2 == 0){
			if(dup2(pipe_out[0], STDIN_FILENO) == -1){
				perror("Dup error: process2");
				
			}		
			closePipes();
			if(execlp(proc2, proc2, NULL)){
				perror("Invalid command process2\n");
				
			}
			
			exit(0);
			
		}

		int code_fork3 = fork();
		if(code_fork3 < 0) {
			perror("Error while forking");
			closePipes();			
			continue;
		}
		if(code_fork3 == 0){
			if(dup2(pipe_err[0], STDIN_FILENO) == -1){
				perror("Dup error ,process3");
				
			}

			closePipes();
					
			if(execlp(proc3, proc3, NULL)){
				perror("Invalid command process3\n");
				
			}	
			exit(0);
			
		}
		closePipes();

		int status;
		wait(&status);
		wait(&status);
		wait(&status);
	}
	
	
	return 0;

}
/*
* Name:                 main
* Input arguments:      none
* Output arguments:     none
* Function:             This function creates the router and handles all child processes,
* 						terminating when the controller is killed by the user.
*/
int main()
{
	pid_t childpid;
	int i,j,k,l,newTabIndex,tailIndex = 0;
	size_t bytesWritten,bytesRead;
	comm = (comm_channel*)calloc (MAX_TAB, sizeof(comm_channel));

	// Create pipes for controller and set to nonblocking read
	if(createPipesAndSetToNonBlock(0) < 0){
		fprintf(stderr, "main@268: Failed to create pipes for controller\n");
		exit(EXIT_FAILURE);
	}
	// Fork to create controller
	if((childpid = fork()) == 0){
		run_control();
	}else{
		
		tailIndex++;		
		child_req_to_parent request;
	
		while(1){
			// Router polls for requests from children (controller + url browsers)
			for(i = 0; i < tailIndex; i++){
				// only read from active processes (indicating pipe is open)
				if(!comm[i].isOpen)	
					continue;
				usleep(1000);

				// Uses non-blocking read 
				bytesRead = read(comm[i].child_to_parent_fd[0], &request, 
					sizeof(child_req_to_parent));

				// continue working with other tabs even if one of them had crashed 
				if( bytesRead == -1 && errno != EAGAIN){
					perror("main@293: Router fails to read tab's request");
					continue;				
				}

				// Router reads a request, act accordingly
				if( bytesRead!= -1){
					switch(request.type){
						case CREATE_TAB:
							// set the new tab index to an appropriate number
							newTabIndex = 0;
							for( l = 1; l < tailIndex; l++){
								if(comm[l].isOpen == false){
									newTabIndex = l;
									break;
								}
							}
							// didn't find an unused slot
							if(!newTabIndex){	
								if(tailIndex <= MAX_TAB)
									newTabIndex = tailIndex++;
								else{
									fprintf(stderr, "main@315: Reached maximum number of tabs\n");
									break;
								}
							}
							// set up pipes for the new tab
							if(createPipesAndSetToNonBlock(newTabIndex) < 0){
								fprintf(stderr, "main@321: Failed to create pipes for new tab\n");
								break;
							}
							// fork to create new tab
							if((childpid = fork()) == 0){
								run_url_browser(newTabIndex);
							}
							break;

						case NEW_URI_ENTERED:
							if(!request.req.uri_req.render_in_tab){
								fprintf(stderr, "main@331: Tab index unspecified\n");
								break;							
							}
							// No url rendering browser existd
							if(tailIndex <= 1){
								fprintf(stderr, "main@336: Create an url window to render the page\n");
								break;
							}
							if(!comm[request.req.uri_req.render_in_tab].isOpen){
								fprintf(stderr, "main@340: The specified tab isn't open\n");
								break;
							}
							if((bytesWritten = write(comm[request.req.uri_req.render_in_tab].parent_to_child_fd[1], 
								&request, sizeof(child_req_to_parent))) == -1){
								perror("main@344: Failed to write url");
							}
							break;

						case TAB_KILLED: //Close file descriptors of corresponding tab's pipes.
							j = request.req.killed_req.tab_index;
							if (j > 0){	// close relevant pipes, kill the process
								if(comm[j].isOpen){
									if(kill_process(j) < 0){
										fprintf(stderr, "main@354: Failed to send kill process request\n");
										fprintf(stderr, "main@354: Cannot kill tab %d process\n",j);
										break;
									}
									if(closePipes(j) < 0){
										fprintf(stderr, "main@359: Tab %d process is killed successfully\n",j);
										fprintf(stderr, "main@359: But failed to close its pipes\n");
										fprintf(stderr, "main@359: OS will close these pipes on program exit\n");
										break;
									}	
									if(j == tailIndex - 1)
										tailIndex--;

								}else{
									fprintf(stderr, "main@368: Tab %d is invalid process to kill\n",j);
									break;
								}
							}else{	
								// Killing controller -> close all tabs  				
								for (k = tailIndex - 1; k > 0; k--){
									if(!comm[k].isOpen)
										continue;
									if(kill_process(k) < 0){
										fprintf(stderr, "main@377: Failed to send kill process request\n");
										fprintf(stderr, "main@377: Cannot kill tab %d process\n",k);
										break;
									}
									if(closePipes(k) < 0){
										fprintf(stderr, "main@382: Tab %d process is killed successfully\n",k);
										fprintf(stderr, "main@382: But failed to close its pipes\n");
										fprintf(stderr, "main@382: OS will close these pipes on program exit\n");
										break; 					
									}	
								}
								closePipes(0);
								exit(EXIT_SUCCESS);
							}	
							break;
						default:
							fprintf(stderr, "main@393: Controller received invalid request\n");
							break;
					}	
				}
			}
		}
	}
	return 0 ;
}